A tree widget for selecting files and directories
PtWidget --> PtBasic --> PtContainer --> PtCompound --> PtGenList --> PtGenTree --> PtFileSel
For more information, see the diagram of the widget hierarchy.
<photon/PtFileSel.h>
The PtFileSel widget is a tree where items can be files, directories, links to files or directories, or custom entries.
A PtFileSel widget.
This widget is useful when a program allows users to open or save files or directories. It reads a directory and displays the contents to the user. Users can also use the widget to navigate a filesystem and choose their own file and directory.
The items in the PtFileSel widget are displayed with images to show what type of file they are. Directory items are expandable and can show the files, directories and links under them. To expand and collapse items, you can use the mouse or these keys:
To: | Press one of: |
---|---|
Expand a directory | Enter, -->, or + on the keypad |
Collapse a directory | Backspace, <--, or - on the keypad |
Each item is stored in a PtFileSelItem_t structure that contains at least:
The widget has two main modes of operation:
A single-level file selector.
In this mode, you can also turn on the "key seek" flag, Pt_FS_SEEK_KEY, which lets you use the keyboard to search for a file. In this mode, the widget selects the next file or directory whose name starts with a character that you type. The search is case-sensitive and wraps to the top of the list if the character isn't found. Whenever you press a key, the Pt_CB_FS_SELECTION callback is invoked with a reason_subtype of Pt_LIST_SELECTION_BROWSE.
The widget also has a useful feature in that it will display file information including name, size, date, permissions, owner and group. You can use the Pt_ARG_FS_FORMAT resource to set the amount and order of information displayed. A PtDivider widget is automatically included in the file selector, along with a label for each column you request.
If you want to override the default headers, create a PtDivider of your own, add appropriate PtLabel widgets to it, and make the divider a child of the file selector. The information you requested will be displayed in the proper columns.
For example, if you create a PtDivider with the headings "Filename", "File size", and "Modification time" as a child of a PtFileSel widget and set the Pt_ARG_FS_FORMAT to nsd, the PtFileSel items would contain the name, size, and date information in the proper columns, but your divider would be displayed.
By default, when you expand an item (directory) for the first time, the directory is read and items are allocated. After this, when you collapse and expand the item, the information is stored in memory to remove the delay of reading a directory. You can refresh an item at any time by using the Pt_ARG_FS_REFRESH resource. You can also set the Pt_FS_FREE_ON_COLLAPSE flag to cause a directory to be re-read every time it's expanded. Every time a new root directory is set, all the previous items are freed.
If you plan to add items to the PtFileSel widget yourself by using the PtFSAllocItem(), PtFSAddFirst(), and PtFSAddAfter() convenience functions, you should have a state callback (Pt_CB_FS_STATE, subtype = Pt_FS_STATE_START) that returns Pt_END.
If you're in tree mode and you get a state callback that is one of your items
(i.e. not from a filesystem), you should deal with the expansion/collapse and
return Pt_END from the callback
to prevent PtFileSel from trying to find the item in the
filesystem.
If you're in single-level mode and you get a state callback for one of your items (i.e. not from a filesystem), you should deal with the expansion; it's your responsibility to free all the previous items. Return Pt_END from the callback as mentioned above. |
In the examples below, Pt_FS_ALL_FLAGS is a value containing all the flag bits. It's used for a mask.
To display only directories, in tree mode:
... PtSetArg(&args[0], Pt_ARG_FS_FLAGS, Pt_FS_SHOW_DIRS, Pt_FS_ALL_FLAGS); PtSetArg(&args[1], Pt_ARG_FS_ROOT_DIR, "/", 0); PtSetArg(&args[2], Pt_ARG_AREA, area, 0); PtCreateWidget(PtFileSel, NULL, 3, args); ...
To display only files in single-level mode, with keyboard seek on:
... PtSetArg(&args[0], Pt_ARG_FS_FLAGS, Pt_FS_SINGLE_LEVEL|Pt_FS_SHOW_FILES| \ Pt_FS_KEY_SEEK, Pt_FS_ALL_FLAGS); PtSetArg(&args[1], Pt_ARG_FS_ROOT_DIR, "/", 0); PtSetArg(&args[2], Pt_ARG_AREA, area, 0); PtCreateWidget(PtFileSel, NULL, 3, args); ...
To display a single level of directories with a ".." to move up levels:
... PtSetArg(&args[0], Pt_ARG_FS_FLAGS, Pt_FS_SHOW_DIRS|Pt_FS_SINGLE_LEVEL, Pt_FS_ALL_FLAGS); PtSetArg(&args[1], Pt_ARG_FS_ROOT_DIR, "/", 0); PtSetArg(&args[2], Pt_ARG_AREA, area, 0); PtCreateWidget(PtFileSel, NULL, 3, args); ...
To display a combination of directories and files in tree mode:
... PtSetArg(&args[0], Pt_ARG_FS_FLAGS, Pt_FS_SHOW_DIRS|Pt_FS_SHOW_FILES, Pt_FS_ALL_FLAGS); PtSetArg(&args[1], Pt_ARG_FS_ROOT_DIR, "/", 0); PtSetArg(&args[2], Pt_ARG_AREA, area, 0); PtCreateWidget(PtFileSel, NULL, 3, args); ...
To show only hidden files (that is, a file whose name begins with a "." and isn't normally displayed):
... PtSetArg(&args[0], Pt_ARG_FS_FLAGS, Pt_FS_SHOW_FILES|Pt_FS_SHOW_HIDDEN, Pt_FS_ALL_FLAGS); PtSetArg(&args[1], Pt_ARG_FS_ROOT_DIR, "/", 0); PtSetArg(&args[2], Pt_ARG_AREA, area, 0); PtCreateWidget(PtFileSel, NULL, 3, args); ...
You can show hidden files or directories by combining the Pt_FS_SHOW_HIDDEN flag with Pt_FS_SHOW_DIRS or Pt_FS_SHOW_FILES.
The PtFileSel widget reads a filesystem, so there could be some delays on large directories. To help you cope with these delays, the widget does the following:
Here's a full PtFileSel example:
#include <stdio.h> #include <Pt.h> #include <photon/PtFileSel.h> PtWidget_t *window, *button, *fs; // quit button callback int quit( PtWidget_t *widget, void *data, PtCallbackInfo_t *info) { exit(EXIT_SUCCESS); return (Pt_CONTINUE); } // open button callback int file_open( PtWidget_t *widget, void *data, PtCallbackInfo_t *info) { PtFileSelItem_t *item; char buffer[PATH_MAX+NAME_MAX + 40]; item = PtFSGetCurrent(fs); if (item == NULL) return(Pt_CONTINUE); strcpy(buffer, "The selected file is\n"); strcat(buffer, item->fullpath); PtAskQuestion(window, "Selected File", buffer, NULL, "Ok", NULL, NULL, 1); return (Pt_CONTINUE); } // state callback, will use the reason to block the // widget for large directory opens int state_cb( PtWidget_t *widget, struct fs_dialog_modal *user, PtCallbackInfo_t *info) { PtArg_t args[3]; PtFileSelCallback_t *it; it = (PtFileSelCallback_t *)(info->cbdata); if (it->reason == Pt_FS_STATE_START) { PtSetArg(&args[0], Pt_ARG_FLAGS, Pt_BLOCKED, Pt_BLOCKED); PtSetArg(&args[1], Pt_ARG_CURSOR_TYPE, Ph_CURSOR_CLOCK, 0); } else { PtSetArg(&args[0], Pt_ARG_FLAGS, ~Pt_BLOCKED, Pt_BLOCKED); PtSetArg(&args[1], Pt_ARG_CURSOR_TYPE, Ph_CURSOR_INHERIT, 0); } PtSetResources(widget, 2, args); return (Pt_CONTINUE); } // function to handle photon draw events and fix screen damage int handler_cb(PtWidget_t *widget, struct fs_dialog_modal *user, PtCallbackInfo_t *info) { PtBkgdHandlerProcess(); return (Pt_CONTINUE); } void main(void) { PtArg_t args[10]; PtCallback_t cb, cb2; PhDim_t win_dim = { 300, 300 }; PhArea_t area; PtFileSelItem_t *item; // make the main window PtSetArg( &args[0], Pt_ARG_WINDOW_TITLE, "PtFileSel Demo", 0 ); PtSetArg( &args[1], Pt_ARG_DIM, &win_dim, 0 ); window = PtAppInit( NULL, NULL, NULL, 2, args ); // make a file selector area.size.w = 200; area.size.h = 200; area.pos.x = 10; area.pos.y = 10; cb.event_f = state_cb; cb2.event_f = handler_cb; PtSetArg(&args[0], Pt_ARG_AREA, &area, 0 ); PtSetArg(&args[1], Pt_ARG_FS_FLAGS, Pt_FS_SHOW_DIRS|Pt_FS_SHOW_FILES, Pt_FS_ALL_FLAGS ); PtSetArg(&args[2], Pt_ARG_FS_ROOT_DIR, "/", 0); PtSetArg(&args[3], Pt_CB_FS_STATE, &cb, 0); PtSetArg(&args[4], Pt_CB_FS_BKGD_HANDLER, &cb2, 0); fs = PtCreateWidget( PtFileSel, window, 5, args ); // make a button for quitting area.size.w = 60; area.size.h = 20; area.pos.x = 230; area.pos.y = 250; cb.event_f = quit; PtSetArg( &args[0], Pt_ARG_AREA, &area, 0 ); PtSetArg( &args[1], Pt_ARG_TEXT_STRING, "Quit", 0); PtSetArg( &args[2], Pt_CB_ACTIVATE, &cb, 0); button = PtCreateWidget( PtButton, window, 3, args ); // make a open button area.size.w = 60; area.size.h = 20; area.pos.x = 160; area.pos.y = 250; cb.event_f = file_open; PtSetArg( &args[0], Pt_ARG_AREA, &area, 0 ); PtSetArg( &args[1], Pt_ARG_TEXT_STRING, "Open", 0); PtSetArg( &args[2], Pt_CB_ACTIVATE, &cb, 0); PtCreateWidget( PtButton, window, 3, args ); PtRealizeWidget( window ); PtMainLoop(); }
Resource | C type | Pt type | Default |
---|---|---|---|
Pt_ARG_FS_FILE_SPEC | char * | String | "*" |
Pt_ARG_FS_FLAGS | ulong_t | Scalar | Pt_FS_SHOW_DIRS | Pt_FS_SHOW_FILES |
Pt_ARG_FS_FORMAT | char * | String | NULL |
Pt_ARG_FS_IMAGES | PhImage_t **, short | Array | PFM-style images (write-only) |
Pt_ARG_FS_REFRESH | PtFileSelItem_t * | Pointer | NULL |
Pt_ARG_FS_ROOT_DIR | char * | String | NULL |
Pt_CB_FS_BKGD_HANDLER | PtCallback_t * | Link | NULL |
Pt_CB_FS_SELECTION | PtCallback_t * | Link | NULL |
Pt_CB_FS_STATE | PtCallback_t * | Link | NULL |
C type | Pt type | Default |
---|---|---|
char * | String | "*" |
A string used to limit the files listed by specifying a pattern that filenames must match. The default is *, but it can be values such as *.c, *.[ch] and so on. You can specify multiple patterns by separating them with a space or a comma (for example, *.gif, *.jpg).
C type | Pt type | Default |
---|---|---|
ulong_t | Scalar | Pt_FS_SHOW_DIRS | Pt_FS_SHOW_FILES |
Flags that control the appearance and behavior of the file selector:
C type | Pt type | Default |
---|---|---|
char * | String | NULL |
A string that's used to set the order and amount of file information displayed and optionally the initial size (in pixels) of each column shown. The following information can be displayed for each item in the widget by including the corresponding letter in the Pt_ARG_FS_FORMAT string:
To display: | Specify: |
---|---|
name | n |
size (in bytes) | s |
size (in kbytes) | k |
date | d |
permissions | p |
owner | o |
group | g |
These letters must be in lower case. |
The s and k options are mutually exclusive. If you try to set both, the first one found in the string is used and the other is ignored. The maximum number of characters is 6; any extra ones are ignored.
If you wish to display only the filename and no divider at the top, set this resource to NULL. To set the size of the column, specify a number of pixels before the corresponding letter. For example, if you want to have the name (100 pixels wide) and the date (200 pixels wide) displayed, set the Pt_ARG_FS_FORMAT resource as follows:
PtSetArg(&args[0], Pt_ARG_FS_FORMAT, "100n200d", 0);
C type | Pt type | Default |
---|---|---|
PhImage_t ** | Array | PFM-style images |
A pointer to an array of image pointers (of type PhImage_t - see the Photon Library Reference) to be used for displaying items. The following constants are used to index into this array (the order shown is the order the images appear in the array):
If you don't want to change an image, specify NULL for that array element. For example, to change the file image, set the Pt_FS_FILE entry of the array to the image pointer and set all others to NULL.
For example, to change the Pt_FS_DIR_OP and Pt_FS_FILE images:
PhImage_t *images[7]; // assume you fill the below image structures PhImage_t new_open_image, new_file_image; ... images[Pt_FS_DIR_OP] = &new_open_image; images[Pt_FS_DIR_CL] = NULL; images[Pt_FS_DLINK_OP] = NULL; images[Pt_FS_DLINK_CL] = NULL; images[Pt_FS_FILE] = &new_file_image; images[Pt_FS_FLINK] = NULL; images[Pt_FS_ERROR] = NULL; PtSetArg(&args[0], Pt_ARG_FS_IMAGES, images, 7); ...
If you want to save processing time, set the length parameter to PtSetArg() to the index of the last image you changed + 1.
Set the flags member of the PhImage_t structures
to:
Ph_RELEASE_IMAGE | Ph_RELEASE_PALETTE | Ph_RELEASE_TRANSPARENCY_MASK | Ph_RELEASE_GHOST_BITMAP before providing the images to the widget. If you do this, the memory used for the images is released when the widget is unrealized or destroyed. |
C type | Pt type | Default |
---|---|---|
PtFileSelItem_t * | Pointer | NULL |
A pointer to the PtFileSelItem_t structure for an expandable item that's to be refreshed (i.e. reread from the disk). For example, if you have a large directory displayed and someone adds a file, you may wish to refresh the directory item. To do this, set the Pt_ARG_FS_REFRESH resource to point to the root item for that subtree.
C type | Pt type | Default |
---|---|---|
char * | String | NULL |
The root directory for the File Selector. The default value is NULL or none.
C type | Pt type | Default |
---|---|---|
PtCallback_t * | Link | NULL |
A list of callbacks invoked each time a directory item is read. For example, if you have 5 files in a directory and expand that directory, this callback is invoked 5 times. It's useful for handling pending Photon events.
Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
For the Pt_FS_NEW_ITEM reason subtype, cbdata points to a structure of type PtFileSelBkgdCallback_t, which contains at least:
If this callback returns Pt_END, the item isn't added. If you wish to translate an item to another encoding, you should use PxTranslate... functions to do so, copy the new string into name, and return Pt_CONTINUE. You may also wish to process events.
Set the Pt_CB_FS_BKGD_HANDLER callback resource before the Pt_ARG_FS_ROOT_DIR resource in order to translate all the filenames. |
Here's an example of this callback:
int handler_cb( PtWidget_t *widget, void *data, PtCallbackInfo_t *info) { PtArg_t args[1]; PtFileSelBkgdCallback_t *it = (void *) info->cbdata; int srctaken = 0, dstmade = 0; char dst[NAME_MAX * 3] = {""}; if (info->reason_subtype != Pt_FS_NEW_ITEM) return (Pt_END); // ctrl is a PxTransCtrl structure that was set with a // call to PxTranslateSet(). The following will convert // the string and copy it back to the original: if (PxTranslateToUTF( ctrl, it->name, strlen( it->name), &srctaken, dst, 0, &dstmade) != -1) strcpy(it->name, dst); PtBkgdHandlerProcess(); return(Pt_CONTINUE); }
C type | Pt type | Default |
---|---|---|
PtCallback_t * | Link | NULL |
A list of callbacks invoked when the user selects an item. Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
These callbacks should return Pt_CONTINUE.
C type | Pt type | Default |
---|---|---|
PtCallback_t * | Link | NULL |
A list of callbacks invoked when an item is expanded or collapsed. Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
If the Pt_FS_SINGLE_LEVEL flag is set in the Pt_ARG_FS_FLAGS resource, item is always NULL because all the previous items are destroyed when you select a new directory in single-level mode. |
These callbacks should return Pt_CONTINUE.
If the widget modifies an inherited resource, the "Default override" column indicates the new value. This modification affects any subclasses of the widget.
The PtFileSel widget defines several convenience functions that make it easier to use the file selector once it's been created. Here's a brief overview: