[Previous] [Contents] [Index] [Next]

Working with Code

PhAB makes it easy to create the user interface for an application. Once PhAB has generated code stubs for your application, you'll need to write the parts that make the application do something. This chapter describes how to work with the code for a PhAB application.

It includes:

Variables and manifests

Widget variables and manifests

PhAB creates global variables and manifests for every module you create, and every widget with a unique instance name. This makes it easier to access the widgets from your application code.

The global variable represents the widget's name, and is defined in the abvars.h file. Each global variable takes this form:


Note: The value of a widget's ABN_... variable is unique only in the module that contains the widget. The variables for widgets in other modules might have the same value.

The manifest represents the widget's instance pointer, and is defined in the abdefine.h file. This file, which is included in all application C files, also defines an external reference to the global variables. Each manifest takes this form:

When PhAB detects a unique instance name it generates a global variable name and a widget manifest. For example, if you change the instance name for a PtButton-class widget to done, PhAB will generate the following:


Note: A widget's global variable and manifest are valid only after the widget has been created, and before it has been destroyed.

Using the global variable and widget manifest

Let's now look at some examples of how you can use the global name and widget manifest within application code. First, here's an example of using the ABN_done global variable and the ApName() function to check for a specific widget in a callback:

int
mycallback( PtWidget_t *widget, ... )

    {

    /* check for specific widget */
    if ( ApName( widget ) == ABN_done ) {
        /* done button processing */
        }

    return( Pt_CONTINUE );
    }

The next example uses ABW_done to change the done widget's background color to red (for more information, see the Manipulating Resources in Application Code chapter):

int
mycallback( PtWidget_t *widget, ... )

    {
    PtArg_t    args[1];

    PtSetArg( &args[0], Pt_ARG_FILL_COLOR, Pg_RED, 0 );
    PtSetResources( ABW_done, 1, args );

    return( Pt_CONTINUE );
    }

Note: Remember that the global variable and the manifest are valid only after the widget has been created and before it has been destroyed.

Handling multiple instances of a window

If you have more than one instance of a window module displayed at a time, then you'll have a problem accessing the widgets in the window. The problem stems from the fact that ABW_instance_name for any widget in a window module points to the last created instance of that widget. If you have more than one instance of the window, then you have more than one instance of the widgets within the window created.

Let's say you have the following window module:


Sample search window


A sample search window.


If you have two instances of it on the screen at the same time and the user clicks on the Search button, how can you get the value in the Name text widget? Since two instances of the window exist, two instances of the text widget exist. ABW_name_txt points to the last instance of the text widget that was created.

The solution lies in the fact that ABN_name_txt can be used to refer to both instances of name_txt, provided you have the widget pointer to the window that contains the desired text widget. This is done using the ApGetWidgetPtr() function:

PtWidget_t  *window_wgt, *text_wgt;

text_wgt = ApGetWidgetPtr(window_wgt, ABN_name_txt);

Where do you get window_wgt? In the above case, you'd have a code callback on the Search button. The first parameter passed to that code callback is the widget pointer to the Search button. You can use ApGetInstance() to get a pointer to the window that contains the Search button.

So the callback would become:

int search_callback( PtWidget_t *widget, ApInfo_t *apinfo, 
                     PtCallbackInfo_t *cbinfo)
{
    char        *name;
    PtArg_t     arg;
    PtWidget_t  *window_wgt, *text_wgt;
        
    // Get the window that the Search button is in
        
    window_wgt = ApGetInstance( widget );
        
    // Given the window, find the text widget
        
    text_wgt = ApGetWidgetPtr( window_wgt, ABN_name_txt );
        
    // Now get the text
         
    PtSetArg( &arg, Pt_ARG_TEXT_STRING, &name, 0 );
    PtGetResources( text_wgt, 1, &arg );

    // The 'name' variable now points to the correct name text
        
    // Process the text as appropriate
    ...

    return( Pt_CONTINUE );
}

Internal link manifests

PhAB generates a manifest for each internal link defined in your application:

For more information about using internal links, see the Accessing PhAB Modules from Code chapter.

Icon manifests

PhAB also provides two manifests to access the application's icons. The names correspond to the default names that you should always use when defining icons:

Global header file

PhAB lets you define one global header file for each application. PhAB generates this file only once, the first time you generate the application's code.


Note: Once you've defined the header, PhAB automatically includes it in any generated C or C++ stub file. So it's best to define the header when you first create the application. See "Specifying application startup information" in the Working with Applications chapter. You can modify the header whenever you need to.

Here's a handy way of using this single header file to simultaneously define all your global variables and the extern references to those variables:

/* Header "globals.h" for my_appl Application */

#include <Pt.h>

#ifdef DEFINE_GLOBALS

#define GLOBAL
#define INIT(x) = x

#else

#define GLOBAL extern
#define INIT(x)

#endif

/* global variables */
GLOBAL int            variable1         INIT(1);   

If DEFINE_GLOBALS is defined, then the last line in the above example looks like:

int variable1 = 1;

If DEFINE_GLOBALS isn't defined, then the last line in the above example looks like:

extern int variable1;

Remember to define all your application's global variables with the GLOBAL prefix, as shown above. Also make sure to include the following line in one (and only one) of your code files:

#define DEFINE_GLOBALS

Including this line ensures that the global variables are defined in this code file and used as extern declarations in all other code files.


Note: In the Makefile, make the code files dependent on the header file. That way, if you make any changes to the header, all the code will be recompiled when you make your application.

Function names and filenames

PhAB generates a function for every initialization function, module setup function, callback, function menu item, and so on you've specified in your application. If you don't need a function, leave its name blank.

After a function has been generated, you're free to modify it. There's just one condition: if you change the function's name, you must also change the name you specified in the link callback or internal link definition. Otherwise, PhAB will continue to regenerate the old name every time it generates the application.

The way you specify the function name in PhAB determines the name of the file the stub is put into:

function_name
Create a C stub file called function_name.c
function_name@filename.ext
Create a stub function and put it in filename.ext. This file will include the headers and function structure required to compile in the Photon environment.
Note: PhAB recognizes .cc, .cpp, and .C as C++ extensions.

If this file already exists, the stub function is added to it. You can use this technique to reduce the number of code files for your application. You can place any number of functions in the same file. We recommend you put all functions related to a module in the same file.

function_name.ext
Short form for function_name@function_name.ext
class::function_name@filename.cc
Generate a stub C++ static member function, but no prototype.
class::function_name@
Don't create a stub function or a prototype. Instead, invoke a C++ static class member function. Prototypes aren't generated for class member functions; your application must have the necessary declarations in its global header file.
function_name@
Generate a prototype for a C function, but not a stub. This is useful if you're using a library of C functions.
::function_name@
Generate a prototype for a C++ function, but not a stub. This is useful if you're using a library of C++ functions.

You can use C and C++ in the same PhAB application. See "What PhAB generates" in the Generating, Compiling, & Running Code chapter.

Multithreaded programs

In general, the Photon libraries aren't threadsafe. In a multithreaded program, only the thread that called PtInit() is allowed to call Photon functions. The only exception is PtPulseDeliver() - you can use it in other threads to send notifications to the Photon thread.

Initialization function

PhAB lets you define an application-level initialization function. The PhAB API calls this function once at startup, before any windows or other widgets are created. For information on setting up this function, see "Specifying application startup information" in the Working with Applications chapter.

The initialization function includes the standard argc and argv arguments so that your application can parse command-line options if needed (see below). You can also use this function to open any widget databases (see the Accessing PhAB Modules from Code chapter) or other application-specific data files.

Here's a sample initialization function generated by PhAB:

/* Initialization Function - init.c                 */
/*                       AppBuilder Photon Code Lib */
/*                                    Version 1.10  */

/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>

/* see Global Header above */
#define DEFINE_GLOBALS

/* Local headers */
#include "globals.h"
#include "abimport.h"
#include "proto.h"

/* Application Options string */
const char ApOptions[] =
    AB_OPTIONS ""; /* Add your options in the "" */

int
init( int argc, char *argv[] )

    {
    char    *device;

    /* Eliminate 'unreferenced' warnings. */
    argc = argc, argv = argv;

    /* Handle command-line options - if required. 
       Remember to ignore the ones handled by Photon. */

    /* Typical spot to open widget databases */

    /* Any other application-specific initialization */

    return( Pt_CONTINUE );

    }

Processing command-line options

PhAB applications have several command-line options by default:

-h height[%]
The height of the window, in pixels, or as a percentage of the screen height if % is specified.
-s server_name
The name of the Photon server:
If server_name is: This server is used:
node_number //node_number/dev/photon
fullpath fullpath
relative_path /dev/relative_path
-w width[%]
The width of the window, in pixels, or as a percentage of the screen width if % is specified.
-x position[%][r]
The x coordinate of the upper-left corner of the window, in pixels, or as a percentage of screen width if % is specified. If r is specified, the coordinate is relative to the current console.
-y position[%][r]
The y coordinate of the upper-left corner of the window, in pixels, or as a percentage of screen height if % is specified. If r is specified, the coordinate is relative to the current console.
-Si|m|n
The initial state of the main window (iconified, maximized, or normal).

You can suppress the options for the application's size and position-see "Command-line options" in the Working with Applications chapter. You can also define additional options.


Note: You should edit the application's usage message to reflect any additions you make. For applications created with PhAB version 1.12 or later, edit the Usemsg file in your application's src directory. For details about the usage message syntax, see usemsg in the QNX Utilities Reference.

Use the getopt() function (described in the Watcom C Library Reference) to process the command-line options. The following example shows how you could process several options (three of which take arguments):

const char ApOptions[] = AB_OPTIONS "a:b:c:pqr";

int init( int argc, char *argv[] ) {
    int opt;
    while ( ( opt = getopt( argc, argv, ApOptions ) ) != -1 )
        switch ( opt ) {
            case 'a' : ...
            case 'b' : ...
            ...
            case '?' : ...
            }
    ...
    return Pt_CONTINUE;
    }

AB_OPTIONS is a macro that defines the default options added by PhAB. It's generated by PhAB, based on your Application Startup settings. For example, if you set the No Pos Arg button, the AB_OPTIONS macro won't contain x: or y:. You can process the options in AB_OPTIONS in two ways:

The PhAB library also looks at the ApOptions array to take into account the options you've added. For example, for the above code the library recognizes that -px100 specifies the X position (along with -p), while -ax100 doesn't.


Note: For applications generated with PhAB 1.11 or earlier, AB_OPTIONS is defined as "s:h:w:x:y:", and a default version of ApOptions in the library is used. If you manually define ApOptions in your program, it's used instead of the default.

Mainloop function

The standard Photon mainloop function, PtMainLoop(), can handle almost every application's requirements. It takes care of all Photon events, non-Photon events, and background processes.

Using the Application Startup Information dialog, you can define your own mainloop function to replace the standard Photon function. But you should use this advanced feature only if your application won't work with the standard PtMainLoop() function. For information on setting up this function, see "Specifying application startup information" in the Working with Applications chapter.

If you specify your own mainloop function, PhAB will generate a mainline stub that contains the standard main-line code. You can modify this code as needed.


Caution: This option is provided for very advanced Photon developers, and requires an excellent understanding of how Photon processes events. It can also make your application incompatible with future versions of Photon. Don't use it unless absolutely necessary.

Module setup functions

A module setup function is generated if you specify a function name in a module-type link callback, as described in "Module callback" in the Editing Resources and Callbacks in PhAB chapter.

All PhAB setup functions have three main arguments:

int
base_setup( PtWidget_t *link_instance, 
            ApInfo_t *apinfo,
            PtCallbackInfo_t *cbinfo )

    {

    /* eliminate unreferenced warnings */
    link_instance = link_instance, 
                    apinfo = apinfo,
                    cbinfo = cbinfo;

    /* your code here */

    return( Pt_CONTINUE );
    }

where:

link_instance
an instance pointer for the PhAB module being created. You'll need to save this pointer if it points to a window module that supports multiple instances.
apinfo
a pointer to a PhAB structure. The structure provides:
cbinfo
a pointer to a common Photon callback structure. The structure provides information related to the widget callback being invoked, the Photon event, and some widget-specific callback data. The format of the data varies with the widget class and callback type. For more info, see PtCallbackInfo_t in the Widget Reference.

Normally, a setup function returns the value Pt_CONTINUE. This tells the PhAB API to continue processing and to display the module being linked. For window and dialog modules, you can return Pt_END to terminate the link callback and destroy the module without displaying it. For window modules, you can return Pt_HALT to neither realize nor destroy the window. This is useful if you want to realize the window later.

Code-callback functions

A code-callback function is generated if you specify a code-type link callback, as described in "Code callbacks" in the Editing Resources and Callbacks in PhAB chapter.

All code-type callback functions have three main arguments:

int
mycallback( PtWidget_t *widget, 
            ApInfo_t *apinfo,
            PtCallbackInfo_t *cbinfo )

    {

    /* eliminate unreferenced warnings */
    widget = widget, 
    apinfo = apinfo, 
    cbinfo = cbinfo;

    /* your code here */

    return( Pt_CONTINUE );
    }

where:

widget
a pointer to the widget instance that invoked the callback.
apinfo
a pointer to a PhAB structure. The structure provides reason codes related to the type of callback function being invoked:
ABR_CANCEL
This callback function is being called by a Cancel link.
ABR_CODE
This callback function is being called by a Code link.
ABR_DONE
This callback function is being called by a Done link.
cbinfo
a pointer to a common Photon callback structure. The structure provides information related to the widget callback being invoked, the Photon event, and some widget-specific callback data. The format of the data varies with the widget class and callback type. For more information, see PtCallbackInfo_t in the Widget Reference.

Your callback should return Pt_CONTINUE unless the description of the callback gives you a reason to return some thing else.

Initializing menus

You may want to do various things to a menu before it's displayed. You can use the menu's setup function to:

You can also use a function menu item to generate new items at runtime.

The methods for doing these things are discussed in the sections that follow.

Enabling, disabling, or toggling menu items

If a menu item isn't currently a valid choice, it's a good idea to disable it so the user won't try to select it. Of course, you'll need to enable it when appropriate, too. If your menu has any toggle items, you'll also need to set them before the menu is displayed. To do these things, use the ApModifyItemState() function.

ApModifyItemState() takes a variable number of arguments:

For example, suppose our application has a menu module whose name is draw_menu, which includes items with the instance names draw_group and draw_align. We can disable these items with the following call:

ApModifyItemState (&draw_menu, AB_ITEM_DIM,
                   ABN_draw_group, ABN_draw_align, NULL);

Changing menu-item text

You can use the ApModifyItemText() function to change the text for a menu item, for example, to replace a command by its opposite. The arguments are as follows:

For example, our Draw menu might have an item that's either Group or Split, depending on what objects the user chooses. We could change the text of the draw_group item in the draw_menu with the following code:

ApModifyItemText (&draw_menu, ABN_draw_group, "Split");

To get the current item text, call ApGetItemText().

Generating menu items

Sometimes you may need to generate the menu items at runtime. For example, PhAB's Window menu includes a list of the modules in your application. To generate menu items, add a function item to your menu module (as described in "Creating function items" of the Working with Modules chapter), and edit the stub function PhAB generates.

For example, if our draw_menu module includes a function item that calls add_shapes(), PhAB generates the following code:

int add_shapes (PtWidget_t *widget, ApInfo_t *apinfo,
                PtCallbackInfo_t *cbinfo)
{

    /* eliminate 'unreferenced' warnings */
    widget=widget, apinfo=apinfo, cbinfo=cbinfo;

    return (Pt_CONTINUE);
}

The parameters passed to this function are of no use.

We use the PtCreateWidget() function to create the menu items, which are usually PtMenuButton widgets. As discussed in the Manipulating Resources in Application Code chapter, we can use the same sort of argument list to set initial values for the resources as we use with PtSetResources(). For example, to add an item Rectangle with a keyboard shortcut of R:

    PtArg_t    args[2];
    PtWidget_t *new_item;

    PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Rectangle", 0);
    PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "R", 0);
    new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);

The second parameter in the call to PtCreateWidget() is the parent of the widget; when you're generating menu items, this should be set to NULL. This makes the new item a child of the current menu or submenu. Don't call PtSetParentWidget() in this case.

Next, we need a callback function for the new item. We have to create this manually; PhAB doesn't create a stub function for it. For example, the callback for our new item could be:

int rect_callback( PtWidget_t *widget,
                   void *client_data,
                   PtCallbackInfo_t *cbinfo )
{
    ...
}

This callback is similar to a code callback generated by PhAB. Its arguments are:

widget
A pointer to the menu item selected.
client_data
Arbitrary data passed to the callback.
Note: This is different from a PhAB code callback, which receives apinfo as its second argument.

cbinfo
a pointer to a common Photon callback structure. The structure provides information related to the widget callback being invoked, the Photon event, and some widget-specific callback data. The format of the data varies with the widget class and callback type. For more info, see PtCallbackInfo_t in the Widget Reference.

The last thing we need to do is add the callback to the menu item's Pt_CB_ACTIVATE callback list, using the PtAddCallback() function:

PtAddCallback (new_item, Pt_CB_ACTIVATE,
               rect_callback, NULL);

The last argument to PtAddCallback() specifies what's to be passed as the client_data argument of the callback. For more information, see "Manipulating callbacks in your code" in the Creating Widgets in Application Code chapter.

Let's put all this together:

int rect_callback( PtWidget_t *widget,
                   void *client_data,
                   PtCallbackInfo_t *cbinfo)
{
    ...
}

int 
add_shapes (PtWidget_t *widget, ApInfo_t *apinfo,
            PtCallbackInfo_t *cbinfo)
{
    PtArg_t    args[2];
    PtWidget_t *new_item;

    /* eliminate 'unreferenced' warnings */
    widget=widget, apinfo-apinfo, cbinfo=cbinfo;

    PtSetArg (&args[0], Pt_ARG_TEXT_STRING, 
              "Rectangle", 0);
    PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "R", 0);
    new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);
    PtAddCallback (new_item, Pt_CB_ACTIVATE,
                   rect_callback, NULL);

    /* Repeat the above for other shapes... */

    return (Pt_CONTINUE);
}

Creating submenus

You can create submenus in the menu created for a menu function item as follows:

  1. Create a menu button for the cascade menu, setting the Pt_ARG_BUTTON_TYPE to Pt_MENU_RIGHT or Pt_MENU_DOWN, as required.
  2. Save a pointer to the current parent widget:
    menu = PtGetParentWidget ();
      
  3. Create a new PtMenu widget and set Pt_MENU_CHILD in the new menu's Pt_ARG_MENU_FLAGS resource.
    Note: PtMenu is a container, so this new menu becomes the current default parent.

  4. Create submenu items, as described above.
  5. Reset the default parent from the saved value:
    PtSetParentWidget( menu );
      
  6. Continue adding items to the top menu, if desired.

This example shows how to generate a submenu, as well as one way the client_data can be used in a generic callback to identify the item chosen from the menu:

/* A menu with a submenu                                 */
/*                            AppBuilder Photon Code Lib */
/*                                         Version 1.11B */

/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>

/* Local headers */
#include "abimport.h"
#include "proto.h"

// Constants for the shapes in the menu
#define RECTANGLE  1
#define CIRCLE     2
#define DOT        3
#define BLOB       4
#define POLYGON    5

int
ShapeMenuCB( PtWidget_t *widget, void *client_data,
             PtCallbackInfo_t *cbinfo )

{
  int shape_chosen = (int) client_data;

  widget=widget, client_data=client_data, cbinfo=cbinfo;

  // This callback uses the client data to determine
  // which shape was chosen.

  switch (shape_chosen) {

    case RECTANGLE: ...
                    break;
    case CIRCLE   : ...
                    break;
    case DOT      : ...
                    break;
    case BLOB     : ...
                    break;
    case POLYGON  : ...
                    break;
    default       : printf ("Unknown shape: %d\n", 
                            shape_chosen);
  }

  return (Pt_CONTINUE);
}

int
add_shapes( PtWidget_t *widget, ApInfo_t *apinfo, 
            PtCallbackInfo_t *cbinfo )

{
  PtArg_t args[3];
  PtWidget_t *menu, *new_item;

  /* eliminate 'unreferenced' warnings */
  widget = widget, apinfo = apinfo, cbinfo = cbinfo;

  PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Rectangle", 0);
  PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "R", 0);
  new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);
  PtAddCallback ( new_item, Pt_CB_ACTIVATE, ShapeMenuCB, 
                  (void *)RECTANGLE );

  PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Circle", 0);
  PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "C", 0);
  new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);
  PtAddCallback ( new_item, Pt_CB_ACTIVATE, ShapeMenuCB, 
                  (void *)CIRCLE );

  // Create a menu button for the submenu.

  PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Miscellaneous", 0);
  PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "M", 0);
  PtSetArg (&args[2], Pt_ARG_BUTTON_TYPE, Pt_MENU_RIGHT, 0 );
  new_item = PtCreateWidget (PtMenuButton, NULL, 3, args);

  // Save the current default parent.

  menu = PtGetParentWidget();

  // Create a submenu. It becomes the new default parent.

  PtSetArg (&args[0], Pt_ARG_MENU_FLAGS, 
      Pt_MENU_CHILD, Pt_MENU_CHILD); 
  new_item = PtCreateWidget (PtMenu, NULL, 1, args);

  // Add items to the submenu.

  PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Dot", 0);
  PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "D", 0);
  new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);
  PtAddCallback ( new_item, Pt_CB_ACTIVATE, ShapeMenuCB, 
                  (void *)DOT );

  PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Blob", 0);
  PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "B", 0);
  new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);
  PtAddCallback ( new_item, Pt_CB_ACTIVATE, ShapeMenuCB, 
                  (void *)BLOB);

  // Restore the current default parent.

  PtSetParentWidget (menu);

  // Continue adding items to the top menu.

  PtSetArg (&args[0], Pt_ARG_TEXT_STRING, "Polygon", 0);
  PtSetArg (&args[1], Pt_ARG_ACCEL_KEY, "P", 0);
  new_item = PtCreateWidget (PtMenuButton, NULL, 2, args);
  PtAddCallback ( new_item, Pt_CB_ACTIVATE, ShapeMenuCB, 
                  (void *)POLYGON);

  return( Pt_CONTINUE );

}

[Previous] [Contents] [Index] [Next]