We recommend that you create your application's UI in PhAB — it's easier than doing it in your code. However, if the interface is dynamic, you'll probably have to create parts of it “on the fly.”
This chapter includes:
Creating a widget in your application code is a bit more work than creating it in PhAB. That's because PhAB looks after a lot of the physical attributes for you, including size, location, and so on. If you create the widget in your code, you'll have to set these resources yourself.
To create a widget in your code, call PtCreateWidget(). The syntax is as follows:
PtWidget_t *PtCreateWidget( PtWidgetClassRef_t *class, PtWidget_t *parent, unsigned n_args, PtArg_t *args );
The arguments are:
You can specify the default parent (used if the parent argument to PtCreateWidget() is Pt_DEFAULT_PARENT) by calling PtSetParentWidget(). To assign a widget to a different container, call PtReparentWidget().
Here are a few things to note about widgets created in application code:
The order in which widgets are given focus depends on the order in which they were created or on the widget order specified in PhAB (see “Ordering widgets” in the Creating Widgets in PhAB chapter). The backmost widget is the first in the tab order; the frontmost widget is the last.
If you're creating widgets programmatically, you can create them in the order in which you want them to get focus, or you can use these functions to change the order:
Alternatively, you can use a widget's Pt_CB_LOST_FOCUS callback (defined by PtBasic) to override the tab order by giving focus to another widget.
In the lost-focus callback, use PtContainerGiveFocus() to give focus to the desired widget, and return Pt_END from the callback to prevent focus from being given to the original target of the focus change.
The Pt_CB_LOST_FOCUS callback is called a second time as focus is removed from the widget to go to the new target. To avoid an endless loop, use a static variable to indicate that this callback has already redirected focus. |
The following functions can be used to work with the widget family hierarchy, and may be useful in setting the focus order:
You can add and remove callbacks in your code as well as from PhAB — just watch for differences between the two types!
An application registers callbacks by manipulating the widget's callback resources. The Photon widget classes employ a naming convention for these resources — they all begin with Pt_CB_.
Callbacks can be added to the callback list kept by these resources using PtAddCallbacks() to add several callback functions to the list or PtAddCallback() to add just one. In either case, the first two arguments to the function are the widget and the name of the callback resource to be augmented. The remaining arguments depend on which function is used.
The third argument to PtAddCallbacks() is an array of callback records. Each record contains a pointer to a callback function and the associated client data pointer that will be passed to the callback function when it's invoked. Each of these callback records is copied to the widget's internal callback list.
For example, we might want to have the application perform some action when the user selects (i.e. presses) a button. The PtButton widget class provides the Pt_CB_ACTIVATE callback resource for notifying the application when the button has been pressed. To create the widget and attach a callback function to this callback resource, we'd have to use code like this:
{ PtWidget_t *button; int push_button_cb( PtWidget_t *, void *, PtCallbackInfo_t *); PtCallback_t callbacks[] = { {push_button_cb, NULL} }; ... button = PtCreateWidget(PtButton, window, 0, NULL); PtAddCallbacks(button, Pt_CB_ACTIVATE, callbacks, 1); }
where push_button_cb is the name of the application function that would be called when the user presses the button. The PtCallback_t structure is used to define lists of callbacks; for details, see the Photon Widget Reference.
When adding only one callback function to the callback list (as in this case), it's simpler to use PtAddCallback(). This function takes the pointer to the callback function as the third argument, and the client data pointer as the final argument. The above code fragment could be written more concisely as:
{ PtWidget_t *button; int push_button_cb( PtWidget_t *, void *, PtCallbackInfo_t *); button = PtCreateWidget(PtButton, window, 0, NULL); PtAddCallback(button, Pt_CB_ACTIVATE, push_button_cb, NULL); }
You can also give an array of callback records as the value for the callback resource when using argument lists in conjunction with PtCreateWidget() or PtSetResources(). Since the callback list is an array, you should specify the array's base address as the third argument to PtSetArg(), and the number of elements as the final argument. In this case, the callback records are added to the current callback list, if there is one. This gives us another way to specify the callback for the above example:
{ PtArg_t arg[5]; int push_button_cb( PtWidget_t *, void *, PtCallbackInfo_t *); PtCallback_t callbacks[] = { {push_button_cb, NULL} }; ... PtSetArg(&args[0], Pt_CB_ACTIVATE, callbacks, 1); PtCreateWidget(PtButton, window, 1, arg); }
Each of these methods has its advantages. PtAddCallback() is of course simple. PtAddCallbacks() is more efficient when there are several callbacks. Using PtSetArg() and passing the result to PtCreateWidget() allows the widget creation and callback list attachment to be performed atomically.
When called, the callback function is invoked with the following parameters:
The client data that's passed to a callback you add from your code isn't the same as the apinfo data passed to a PhAB callback. |
The PtCallbackInfo_t structure is defined as:
typedef struct Pt_callback_info { unsigned long reason; unsigned long reason_subtype; PhEvent_t *event; void *cbdata; } PtCallbackInfo_t;
The elements of PtCallbackInfo_t have the following meaning:
For more information, see the descriptions of the callbacks defined for each widget in the Widget Reference.
You can remove one or more callbacks from a callback list associated with a widget resource using the PtRemoveCallbacks() and PtRemoveCallback() functions.
Don't try to remove a callback that was added through PhAB; unexpected behavior may result. |
PtRemoveCallbacks() takes an array of callback records as an argument and removes all the callbacks specified by it from the callback list. PtRemoveCallback() removes just one callback function from the callback list. Both functions take the widget as the first argument and the widget resource as the second argument.
To remove the callback from the button we've created above, we could do this:
int push_button_cb( PtWidget_t *, void *, PtCallbackInfo_t *); PtCallback_t callbacks[] = { {push_button_cb, NULL} }; PtRemoveCallbacks(button, Pt_CB_ACTIVATE, callbacks, 1);
or this:
int push_button_cb( PtWidget_t *, void *, PtCallbackInfo_t *); PtRemoveCallback(button, Pt_CB_ACTIVATE, push_button_cb,
Both the callback function pointer and the client data pointer are important when removing callbacks. Only the first element of the callback list that has both the same callback function and the same client data pointer will be removed from the callback list.
You can examine the callback list by getting the value of the appropriate callback list resource. The type of value you get from a callback list resource is different from the value used to set the resource. Although this resource is set with an array of callback records, the value obtained by getting the resource is a pointer to a list of callback records. The type of the list is PtCallbackList_t. Each element of the list contains a cb member (i.e. the callback record) and a next pointer (which points to the next element of the list).
The following example shows how you can traverse through the Pt_CB_ACTIVATE callback list for widget to find all instances of a particular callback function, cb:
... PtCallbackList_t *cl; PtGetResources(widget, Pt_CB_ACTIVATE, &cl, 0); for ( ; cl; cl = cl->next ) { if ( cl->cb.func == cb ) break; }
You can add and remove event handlers (raw and filter callbacks) in your application code as well as in PhAB — however, there are some differences between the two types.
For a description of raw and filter callbacks and how they're
used, see
“Event handlers — raw and filter callbacks”
in the Events chapter.
For information on adding event handlers in PhAB, see “Event handlers — raw and filter callbacks” in the Editing Resources and Callbacks in PhAB chapter. |
As with callbacks, you can also set or examine event handlers by performing a set or get directly on the event handler resource. The following resources of PtWidget let you specify handlers for Photon events:
For more information about these callback resources, see the Photon Widget Reference.
The set operation requires an array of event handler records of type PtRawCallback_t. These are similar to the callback records mentioned above, having event_mask, event_f, and data fields.
The event mask is a mask of Photon event types (see PhEvent_t in the Photon Library Reference) indicating which events will cause the callback function to be invoked. The event_f and data members are the event handler function and client data, respectively.
If you add an event handler to a realized widget and the widget's
region isn't sensitive to one or more of the events
contained in the event mask, then the region is made sensitive to them.
If you add the event handler before realizing the widget, you have to adjust the region's sensitivity yourself after realizing the widget. See PhRegionChange() in the Photon Library Reference. |
A get operation yields a PtRawCallbackList_t * list of event handler records. As with callback lists, the list contains two members: next and cb. The cb member is an event handler record.
You can add Pt_CB_RAW event handlers using either the PtAddEventHandler() or PtAddEventHandlers() function.
You can add Pt_CB_FILTER event handlers using either the PtAddFilterCallback() or PtAddFilterCallbacks() function.
The arguments to PtAddEventHandler() and PtAddFilterCallback() are:
The arguments to PtAddEventHandlers() and PtAddFilterCallbacks() are:
You can remove Pt_CB_RAW event handlers by calling either PtRemoveEventHandler() or PtRemoveEventHandlers().
You can remove Pt_CB_FILTER event handlers by calling either PtRemoveFilterCallback() or PtRemoveFilterCallbacks()
Don't remove event handlers that were added through PhAB; unexpected behavior may result. |
The parameters to PtRemoveEventHandler() and PtRemoveFilterCallback() are:
This looks for an event handler with the same signature — i.e. the same event_mask, data and event_f — in the widget and removes one if it's found.
The parameters to PtRemoveEventHandlers() and PtRemoveFilterCallbacks() are:
As with PtRemoveEventHandler() and PtRemoveFilterCallback(), an event handler is removed only if it has the exact same signature as one of the event handler specifications in the array of event handler records.
When invoked, event handlers receive the same arguments as callback functions, i.e. the parameters are:
The client data passed to this event handler isn't the same as the apinfo data passed to an event handler added through PhAB. |
Event handlers return an integer value that the event handler must use to indicate whether or not further processing should be performed on the event. If the event handler returns the value Pt_END, this indicates that no further processing is to be performed on the Photon event, and the event is consumed.
The event member of the info parameter contains a pointer to the event that caused the event handler to be invoked. You should check the type member of this event to determine how to deal with the event. It will be one of the event types specified in the event_mask given when the event handler was added to the widget.
To retrieve the data associated with the particular event, call the PhGetData() with the pointer to the event as a parameter. This will return a pointer to a structure with the data specific to that particular event type. This structure's type depends on the event type.
Widget class styles let you customize or modify a widget's appearance, size, and behavior at runtime. They also let multiple looks for a single type of widget exist at the same time. Essentially, a widget class style is a collection of methods and data that define the look and feel of instances of the widget class.
Each widget class has a default style, but you can add or modify an arbitrary number of additional styles at any time. You can even modify the default style for a class, changing the look and feel of any instances of that class that are using the default style.
Each instance of a widget can reference a specific style provided by its class. You can change the style that any widget is using whenever you want.
Each style has a set of members, including a name for the style and functions that replace or augment some of the widget class's methods. Methods are class-level functions that define how the widget initializes itself, draws itself, calculates its extent, and so on. For more information about methods, see the Building Custom Widgets guide.
The members of a style are identified by the following manifests:
For details about the members, see PtSetStyleMember().
The following functions let you create and manipulate the widget class styles:
Some of these functions require or return a pointer to a PtWidgetClassStyle_t structure. Don't access the members of this structure directly; call PtGetStyleMember() instead.
You can also set the style for a widget instance by setting its Pt_ARG_STYLE resource (see PtBasic in the Widget Reference). Setting this resource has the same effect as calling PtSetWidgetStyle(). |
This example creates a style called blue and some buttons. Note that your widgets can use a style before you've added the style to the class or even before you've created the style. When you do create the style and add it to the class, any widgets that use the style are updated immediately.
#include <Pt.h> PtWidget_t *win, *but; PtWidgetClassStyle_t *b; void blue_draw (PtWidget_t *widget, PhTile_t *damage) { /* This is the drawing function for the blue style. It draws a blue rectangle (without a label) for the widget. */ PgSetFillColor( Pg_BLUE); PgDrawRect( PtWidgetExtent (widget,NULL), Pg_DRAW_FILL); } int use_blue_style( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo) { /* This callback sets the current style for the given widget instance. If you haven't attached the blue style to the class, there shouldn't be any change in the widget's appearance. */ PtSetWidgetStyle (widget, "blue"); return Pt_CONTINUE; } int attach_blue_style( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo) { /* This callback adds the style to the widget class. If you've clicked on one of the "Use blue style" buttons, the style of all buttons should change. */ PtAddClassStyle (PtButton, b); return Pt_CONTINUE; } int main() { PhArea_t area = {{0,50},{100,100}}; PtArg_t argt[10]; PtStyleMethods_t meth; PtCallback_t cb = {use_blue_style, NULL}; PtCallback_t cb2 = {attach_blue_style, NULL}; int unsigned n; /* Initialize the methods for the style. */ meth.method_index = Pt_STYLE_DRAW; meth.func = blue_draw; PtInit(NULL); /* Create the window. */ PtSetArg (&argt[0], Pt_ARG_DIM, &area.size, 0); win = PtCreateWidget (PtWindow, NULL, 1, argt); /* Create some buttons. When you click on one of these buttons, the callback makes the widget instance use the blue style. */ n = 0; PtSetArg (&argt[n++], Pt_ARG_TEXT_STRING, "Use blue style", 0); PtSetArg (&argt[n++], Pt_CB_ACTIVATE, &cb, 1); but = PtCreateWidget (PtButton, NULL, n, argt); PtSetArg (&argt[0], Pt_ARG_TEXT_STRING, "Use blue style also", 0); PtSetArg (&argt[n++], Pt_ARG_POS, &area.pos, 0); but = PtCreateWidget (PtButton, NULL, n, argt); /* Create another button. When you click on it, the callback attaches the blue style to the widget class. */ n = 0; PtSetArg (&argt[n++], Pt_ARG_TEXT_STRING, "Attach blue style", 0); PtSetArg (&argt[n++], Pt_CB_ACTIVATE, &cb2, 1); PtSetArg (&argt[n++], Pt_ARG_POS, &area.pos, 0); area.pos.y = 85; but = PtCreateWidget (PtButton, NULL, n, argt); /* Copy the default style to make the blue style. Replace the drawing member of the new style. */ b = PtDupClassStyle (PtButton, NULL, "blue"); PtSetClassStyleMethods (b,1,&meth); PtRealizeWidget (win); PtMainLoop(); return EXIT_SUCCESS; }
Photon provides a mechanism for you to allow a block of user code to be pulled in and executed during the initialization of Photon applications. This functionality is most frequently used to customize widget styles, allowing you to change the appearance and behavior of widgets without having to re-compile, re-link, or otherwise reconstruct executables.
The Photon hook can be used for many other things besides widget styles. For example, it can be used to log application usage information, or for more complicated situations such as remote control of an application. |
PtInit() looks for a DLL, PtHook.so, in the search path, and executes the symbol for PtHook() in the DLL.
You can use the pt_multihook.so DLL and rename it as PtHook.so to load one or several DLLs, pointed to by the PHOTON_HOOK environment variable. If PHOTON_HOOK points to a DLL, that DLL is loaded and its PtHook() function is executed. If PHOTON_HOOK points to a directory, each DLL in it is loaded and its PtHook() function executed.
The PtHook.so feature may introduce security holes if the DLL code is insecure. If you use the pt_multihook.so, you may wish to modify its code to add your own security features. See the code listing below. |
Example PtHook.so - the pt_multihook:
#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <dirent.h> #include <photon/PtHook.h> static int hookit( const char *hookname, PtHookData_t *data ) { void *handle; if ( ( handle = dlopen( hookname, 0 ) ) == NULL ) return -1; else { PtHookF_t *hook; if ( ( hook = (PtHookF_t*) dlsym( handle, "PtHook" ) ) == NULL || (*hook)( data ) == 0 ) dlclose( handle ); return 0; } } int PtHook( PtHookData_t *data ) { const char *hookname; DIR *dir; if ( ( hookname = getenv( "PHOTON_HOOK" ) ) != NULL && hookit( hookname, data ) != 0 && ( dir = opendir( hookname ) ) != NULL ) { struct dirent *de; while ( ( de = readdir( dir ) ) != NULL) if ( de->d_name[0] != '.' ) { char path[512]; if ( (unsigned) snprintf( path, sizeof(path), "%s/%s", hookname, de->d_name ) < sizeof(path) ) hookit( path, data ); } closedir( dir ); } return Pt_CONTINUE; }
The PtHook function, declared in Photon/PtHook.h, looks like this:
int PtHook( PtHookData_t *data );
PtHookData_t has at least these members:
The function can return Pt_END to ensure the DLL is not unloaded by PtInit(), or return Pt_CONTINUE to ensure DLL is unloaded.
Here is a simple example of changing widget styles using the Photon Hook. The following code changes the fill for all buttons to blue, based on the previous widget style example.
To compile this code, use:
cc -shared button_sample.c -o PtHook.so
Place the PtHook.so in the search path to change the button style for all Photon applications. You can get the search path with getconf _CS_LIBPATH.
#include <Pt.h> static void (*button_draw)(PtWidget_t *widget, PhTile_t const *damage ) = NULL; void blue_draw (PtWidget_t *widget, PhTile_t *damage) { /* This is the drawing function for the blue style. It draws a blue rectangle (without a label) for the widget. */ PgSetFillColor( Pg_BLUE); PgDrawRect( PtWidgetExtent (widget,NULL), Pg_DRAW_FILL); } int PtHook (void *data) { PtStyleMethods_t button_meth = { Pt_STYLE_DRAW, blue_draw }; PtWidgetClassStyle_t *button_style = PtFindClassStyle( PtButton, NULL ); button_draw = button_style->draw_f; PtSetClassStyleMethods( button_style, 1, &button_meth ); return( Pt_END ); }