Chapter 8. Component Libraries

So far, this manual has described how to use RapidApp to create complete applications. However, you can also use RapidApp to create libraries of components that you can reuse and share with other developers. This chapter describes how to create component libraries, how to load components onto RapidApp palettes from component libraries, and how to import components into RapidApp that you didn't create in RapidApp.

You can also arrange for RapidApp to create components that are subclassed from classes you have written, in addition to the VkComponent, VkSimpleWindow, and VkWindow classes provided by ViewKit. This chapter describes how to integrate such classes with RapidApp.

Work With Component Libraries

Introduction

By default, RapidApp is configured to create a standalone application, and all of the techniques described so far in this manual (for example, starting by creating top-level windows) assume that you are creating an application. As a result, the Makefile generated by RapidApp creates a single executable file; any components that you create for your application are compiled and included in the application rather than in a separate library. Furthermore, the auxiliary files, for example those generated for use with Software Packager, assume a target application rather than a library.

This section describes how to configure RapidApp to create a component library and how to generate the component library. It also provides a simple example.

Configuring RapidApp

Generating a component library with RapidApp rather than an application requires you to follow just a few simple steps:

  1. Configure RapidApp to generate a library instead of applications. To do so:

    • From the File menu, choose “Preferences” to display the Preferences dialog.

    • In the Project card, provide the name of a library in the Library name text field. Use the standard library naming format, such as “libXYZ,” but don't provide a suffix. RapidApp ignores any suffix you include.

    • Provide the name of a header directory in the Library headers text field. Enter the name of a subdirectory relative to /usr/include (but don't include “/usr/include” as part of the directory name). When you install your library on your system or create an installable version of your library for other developers, your library's header files are installed in this subdirectory of /usr/include. For example, IRIS ViewKit places its headers in /usr/include/Vk, whereas IRIS IM places its header files in /usr/include/Xm.

  2. Create your components. Normally, you can create them directly on the desktop. You don't need to create them as children of Simple Window or VkWindow objects, although you can if you wish.

  3. Place default resources in classes rather than an application resource file. This encapsulates the default resource with the components rather than making them dependent on an external resource file. To do so:

    • From the File menu, choose “Preferences” to display the Preferences dialog.

    • In the Code Style card set the “Place resources in classes” option.

  4. (Optional) If you create components as children of Simple Window or VkWindow objects, you probably want to prevent RapidApp from automatically adding callback functions to your component to support the pass-through of menu callbacks (described in “VkWindow”). Normally, this feature isn't useful for a standalone component. To do so:

    • Select the window object containing your component.

    • Set the window's autoRouteCallbacks resource to False.

Build a Component Library

Once you have configured RapidApp as described in “Configuring RapidApp” and created some components, you can generate code and build a component library. This section includes the following:

Generating Code for the Component Library

To generate code for the library select “Generate C++” from the Project menu, just as if you were generating code for an application.

RapidApp generates all the files it would for an application. However, the Makefile that RapidApp generates differs in several ways from the Makefile it would generate for an application:

  • It contains as targets both the static and shared libraries.

  • The application target links with the library rather than compiling the components directly into the application.

  • It contains an “install” target that installs the libraries in /usr/lib and the class headers in the subdirectory of /usr/include specified by the Library headers text field of the Project card in the Preferences dialog, but doesn't install the application itself or any of its support files (for example, its resource file).

Furthermore, the files used by Software Packager for creating an installable image differ in several ways from those generated for an application:

  • They install the libraries in /usr/lib.

  • They install the class headers in the subdirectory of /usr/include specified by the Library headers text field of the Project card in the Preferences dialog.

  • They don't include the application, its resource file, its icon, or its FTR file for installation.

Building the Component Library

When you select “Build Application” from the Project menu, RapidApp creates both a static library (a library that is statically bound to a program compiled with it) with a .a suffix and a shared library (a library that binds with a program at run time) with a .so suffix. The library includes both the UI classes and their derived subclasses.


Note: RapidApp doesn't include dialogs, top-level windows, or application objects (instances or subclasses of VkApp) in the library.

RapidApp also builds an application, but you should think of this application as a test driver rather than a full application. RapidApp links the component library with the application rather than compiling the components directly into the application. This application displays windows only if you didn't unset the “Create windows for orphaned objects” option in the RapidApp card of the Preferences dialog (as described in “Configuring RapidApp”).

If you try to run the application from the command line rather than by selecting “Run Application” from the Project menu, you may get an error message such as:

7054:calculator: rld: Fatal Error: cannot map soname 'libcalc.so' using any of the filenames /usr/lib/libcalc.so:/lib/libcalc.so

This is because the application needs the shared library (libcalc.so in this example) to run. This library is loaded at run time by rld, the run-time loader. By default, rld looks in /usr/lib but not in the current directory. If you want to run the program from a shell, there are two approaches you can take:

  • Install the library by entering make install in a shell. This places the shared library in /usr/lib. (You might need root permission to do this). This step also installs header files, which you might or might not want to do. Entering make install doesn't install the application.

  • Set the environment variable LD_LIBRARY_PATH to “.” to change the search path rld uses to include the current directory. This allows you to run the application without installing libraries or headers on your system.

Notice that if you use the first option, you need to reinstall the library any time you change it for the changes to be reflected in the test application. If you run the application from RapidApp, or set the LD_LIBRARY_PATH before running the program from a shell, changes are reflected as soon as you rebuild the library.

Example of Creating a Component Library

As an example of creating a component library, consider the Calculator component as created in “Creating Components”. After creating the Calculator component, you can create a library containing it by following these steps:

  1. From the File menu, choose “Preferences” to display the Preferences dialog.

  2. In the Project card, enter a library name in the Library name text field. In this case, enter “libcalc.”

  3. Enter the name of a header directory in the Library headers text field. In this case, enter “CalcLib” so that the header files for the Calculator component appear in /usr/include/CalcLib.

  4. Select “Generate C++” from the Project menu to generate code for the component.

  5. Select “Build Application” from the Project menu to compile the static and shared libraries. The libraries contain both the Calculator and CalculatorUI classes.

Install a Component Library

You can install the component library and associated headers on your system simply by entering make install in a shell window. You can then use your library as you would any other library.


Note: You don't need to install the library and headers on your system to create an installable image for distribution to others.


Package a Component Library for Distribution

Once you have created and tested your library, you can use Software Packager to package your library and associated class headers for installation by others. To do so, select “Edit Installation” from the Project menu to launch Software Packager, just as if you were packaging an application for distribution.

RapidApp automatically generates files for Software Packager so that it installs the libraries in /usr/lib and the class headers in the subdirectory of /usr/include specified by the Library headers text field on the Project card in the Preferences dialog. Neither the test application nor any of its associated support files (for example, its resource file, its icon, or its FTR file) are included in the installable image. You can either use Software Packager to generate an installable image for distribution, or you can simply enter make images in a shell window in your project directory. For complete instructions for using Software Packager, consult the Software Packager User's Guide; Chapter 1, “Packaging Software for Installation: An Overview.”


Note: You don't need to install the library and headers on your system to create an installable image for distribution to others.


Load Components Onto Palettes

This section includes the following:

Introduction

Once you create component libraries and install them on your system, you can load them onto RapidApp palettes and use them as you would any other component. These components that you load into RapidApp are “live”: they actually operate when RapidApp is in play mode. You can even extend the components to add resources that you can set and change using the RapidApp resource editor.

Using this feature of RapidApp, you can create entire custom palettes of user-created components that you can use to create applications. For example, if your organization has a group producing common services for use by others in creating applications, this group could develop various user interface elements to be used throughout one or more projects and make the elements available to the other groups as libraries of components.

You can also use the techniques described in this section to load some components that you didn't create with RapidApp. However, externally created components must follow several guidelines for RapidApp to be able to load them. “Load Non-RapidApp Components” describes how to write an external component so that you can load it onto a RapidApp palette.

Note that loading components differs from simply using “Import” from the File menu to import one or more components in two ways:

  • The “Import” option loads only the .uil file description of the components, not any functional code that you have added. You can't access any of the functionality of the components that you add unless you also copy all of the header and source files for those components to your project directory.

  • When you generate code, RapidApp regenerates code for the components you import. If you have copied the source and header files for those components to your project directory, RapidApp integrates any code changes with the source files. With “live” components, RapidApp simply links with the library that contains the component.

Loading Components Into RapidApp

This section describes how to load a component onto a RapidApp palette, gives an example of doing so, and describes the files generated when you do so.

Components that you create with RapidApp are normally very easy to load onto a RapidApp palette. You can load any self-contained component, with the exception of top-level windows, such as VkWindow or Simple Window, and dialogs. Before loading a component into RapidApp, you should consider testing it using the Component Tester application shipped with RapidApp, as discussed in “Testing Components”.

The following sections are included:

Loading a Component Onto a RapidApp Palette

Before loading a component onto a RapidApp palette, you must create and install a shared library containing that component, as described in “Work With Component Libraries”.

Note: You must be sure that all classes you wish to load into RapidApp are marked as “Dynamic” classes. This option is available when you initially create the class as well as in the Class Header when working in Class Edit mode. All classes are dynamic by default, except for subclasses of VkSimpleWindow, VkWindow, and dialogs.

Once you have created the library, you can add any component it contains by following these steps:

  1. Select “Install Classes” from the Classes menu to display the RapidApp Component Importer dialog, as shown in Figure 8-1.

    Figure 8-1. The RapidApp Component Importer Dialog


  2. In the dialog, fill in each text field. If you are currently working on the project that contains the components in RapidApp, all fields will be filled in with initial values for one of the classes. Other classes can be accessed by selecting the “Browse...” menu item from the “Classes” menu. The values provided are defaults, and can be changed if needed. You need to provide:

    Class Name 

    The name of the component to import

    Library Path and Name 


    The name of the library containing the component

    Library to be Linked 


    The link specification that you use to link with this library

    Header File 

    The header file for the component, specified relative to /usr/include

    Other Required Headers 


    Any other header files required to use the component, specified relative to /usr/include. If the component requires multiple header files, separate the file names with spaces or commas.

    Palette 

    The name of the palette on which this component should appear

    Icon Name 


    The name that should appear on the palette. For long names, you can use an underscore to separate words. RapidApp converts the underscore to a newline when displaying the component. RapidApp automatically inserts underscores in mixed-case names.

    Desktop Icon Drop List
    It's possible to set various resources of an interface element or class by dragging items from the desktop onto the element or class. For example, you can set the labelPixmap resource of a button by dragging a pixmap file onto the button. You can set the fileName resource of a scene viewer by dragging an inventor file onto the scene viewer.
     

     

    To give this capability to a user-defined component, provide a list of file types and the corresponding “resource.” The file type must be a name recognized by the SGI Indigo Magic desktop. The resource must be the name of a resource which corresponds to a method that accepts a filename. File types and resources are separated by colons, and pairs are separated by commas. For example:

    TiffImageFile:setImageFile, XPMPixmapFile:setPixmap
    

     

    Some components take advantage of this feature to make it easier to manipulate the component in RapidApp. For others, it's ignored. This feature does not affect programs built with the component.

  3. Install or save the component information:

    • If you want to install the component on your local system so that RapidApp can use it, click the Install Locally. RapidApp saves the information in several files in your personal $HOME/.rapidappdir directory. RapidApp also saves the files in the current directory and updates the files used by Software Packager so that the installable image you create contains the files as well. “Files Generated When Loading Components on a Palette” describes these files in more detail.

    • If you want only to save the component information to the current directory, but don't want to install the files in your personal $HOME/.rapidappdir directory, click the Save/Generate button. This creates the files you will need to create in installation package you can install on other machines.

  4. Repeat steps 2 and 3 for any additional components you want to load onto RapidApp palettes. You can select new classes from the list of available classes, which can be displayed by selecting the Browse... menu item.

  5. Click the Close button when you are finished specifying components to load.

  6. Quit and restart RapidApp. The components you installed should now appear on the palettes you specified.


Note: Clicking the Install button saves the component information in several files in your personal $HOME/.rapidappdir directory, which RapidApp reads when it starts. Because this information is stored in your home directory, it affects RapidApp only when you use RapidApp from your account. If you start RapidApp from another account on your system, the components don't appear. To install components so that they appear globally (that is, when you start RapidApp from any account on your system), you must create an installable image and load that image on your system, as described in “Creating an Installable Image”.


Example of Loading a Component Onto a RapidApp Palette

As an example, consider the case where you want to load the Calculator component onto a RapidApp palette. To do so, create the libCalc library as described in “Example of Creating a Component Library”, fill out the RapidApp Component Importer dialog as shown in Figure 8-2, and click the Install button. The next time you start RapidApp, it contains a MyComponents palette with a component on it that reads “My Simple Calculator.”

Figure 8-2. Example of Specifying a Component to Load on a RapidApp Palette


You can now use the Calculator component just like any built-in interface element. If you place a Calculator component in your interface and then switch to play mode, the Calculator component actually adds numbers. Furthermore, if you create an application using the Calculator, RapidApp doesn't generate code for the Calculator component. Instead it links with the libcalc.so library and uses the component found in that library.

Files Generated When Loading Components on a Palette

RapidApp generates several component description files when you use the RapidApp Component Importer dialog to load components into RapidApp. In the following descriptions, <ClassName> represents the name of the component you are loading:

<ClassName>.item 


A file that describes an item on a palette. The item file specifies information about the component including its name, its palette's name, and the pixmap used for the icon. RapidApp places this file in the $HOME/.rapidappdir/items directory when you click the Install button in the RapidApp Component Importer dialog.

<ClassName>.col 


A “collection file” that describes the component in more detail and sets some basic resources, such as the initial default size. This file also contains much of the information provided in the RapidApp Component Importer dialog, including the name of the library, the header file, and so on. RapidApp uses this information to load the component from the shared library when you create an instance of the component in an interface, and also uses the information for code generation. The item file tells RapidApp which collection file to load. RapidApp places this file in the $HOME/.rapidappdir/collections directory when you click the Install button in the RapidApp Component Importer dialog.

<ClassName>.pix 


The pixmap that appears on the palette for this component. The item file indicates the name of the pixmap file. RapidApp provides a default pixmap for the component in Xpm format. You can edit this file to provide a unique icon for your component. RapidApp places this file in the $HOME/.rapidappdir/pixmaps directory when you click the Install button in the RapidApp Component Importer dialog.

<ClassName>.wml 


A file that describes the Calculator component using a “Widget Metal Language” (wml). RapidApp requires this information for internal use; you shouldn't need to modify this file for any reason. RapidApp places this file in the $HOME/.rapidappdir/wml directory when you click the Install button in the RapidApp Component Importer dialog.

<ClassName.xres> 


A file that contains X-resource style descriptions of the help text RapidApp should display when the pointer moves over the component's icon, or over resources supported by the component.

On startup, RapidApp reads the contents of $HOME/.rapidappdir/items and uses the information in each item file that it finds to load the other associated files.


Note: Clicking the Install button in the RapidApp Component Importer dialog saves the component description files in the appropriate subdirectories of your personal $HOME/.rapidappdir directory. Because this information is stored in your home directory, it affects RapidApp only when you use RapidApp from your account. If you start RapidApp from another account on your system, the components don't appear. To install components so that they appear globally (that is, when you start RapidApp from any account on your system), you must create an installable image and load that image on your system, as described in “Creating an Installable Image.”


Deleting a Component from a Custom RapidApp Palette

You can delete a component from a personal custom palette by choosing the class to be removed from the Browser list and selecting the Remove Local item in the Classes menu.

You can also remove a component from your personal palette by deleting all of the component description files in the $HOME/.rapidappdir configuration directory. For example, to delete the component MyComponent, enter the following

% cd ~/.rapidappdir 
% rm */MyComponent.* 

To delete component that you installed globally from an installable image, simply use Software Manager to remove that installable image from your system.

Creating an Installable Image

The procedures described in “Loading Components Into RapidApp” allow you to load components onto RapidApp palettes on your system. However, you might also want to distribute these components to others. To do so, you must create an installable image of the library and the component description files:

  1. Create a component library as described in “Build a Component Library”.

  2. Install the component library on your system as described in “Install a Component Library”.

  3. Use the RapidApp Component Importer dialog to save (or install and save) component description files for each component you want to add to a RapidApp palette, as described in “Loading Components Into RapidApp”. RapidApp automatically updates the files used by Software Packager so that the installable image you create contains the component description files as well.

  4. Either use Software Packager to generate an installable image for distribution or simply enter make images in a shell window in your project directory. (For complete instructions for using Software Packager, consult the Software Packager User's Guide; Chapter 1, “Packaging Software for Installation: An Overview.” ) The installable image places the component description files in /usr/lib/RapidApp, the same location RapidApp places its built-in components. After installing the libraries, the components automatically appear on their appropriate palettes whenever you start RapidApp.

Adding Resources to Components

When you load a component onto a RapidApp palette, you can create instances of the component and change the geometry of those instances. By default, you can't alter any of the other characteristics of the component; there are no built-in resources that you can change to affect the display and behavior of the component. However, you can add resources to your component that appear in the RapidApp resource editor. You can then select an instance of a component and change those resources to modify the appearance or behavior of the component.

In a component, resources correspond to C++ member functions. There are a few restrictions on the type and style of these member functions:

  • Each member function must have a void return type

  • The member function must not be virtual

  • A member function may take exactly one argument, which must have one of the following types:

    • const char *: any string

    • int: An integer value

    • float: a floating point value

    • Boolean: a boolean value

    • An enum: an enumerated value, which must be zero-based.

A few other types are also possible, for special purposes. See “Arguments and Argument Types” for details.

To illustrate adding resources to a component, first create a simple component called LabeledText to which you'll add a couple of resources. The component consists of a label and a text field in a RowColumn container. Then add two resources to set the text shown in the label and to toggle the text field between editable and read-only mode.

To create the LabeledText component and add the desired resources:

  1. Set up the application options:

    • From the File menu, choose “Preferences” to display the Preferences dialog.

    • In the Project card, enter liblbtext in the Library name text field.

    • Enter LibText in the Library headers text field.

  2. Create the component interface:

    • Create a RowColumn container. Set its orientation resource to XmHORIZONTAL.

    • Add a Label control to the RowColumn container.

    • Add a Text Field control to the RowColumn container.

    • Select the RowColumn container and then select “Make Class” from the Classes menu. In the Make Class dialog, enter “LabeledText” as the class name. Your interface should look similar to that shown in Figure 8-3.

      Figure 8-3. LabeledText Component


  3. Save the interface and generate code:

    • Select “Save” from the File menu to save the interface.

    • Select “Generate C++” from the Project menu to generate the files for the component.

  4. Create public member functions of the class to set the resources. The functions must have void return values and accept a single argument, whose type may be String, Boolean, integer, float, filename, or an enumerated type.

    • Add declarations for two public member functions in the file LabeledText.h:

      void setLabel(const char *); 
      void setReadOnly(Boolean); 
      

    • Add the source for the member functions in the file LabeledText.C:

      void LabeledText::setLabel(const char * str) 
      { 
          XmString xmstr = XmStringCreateLocalized( (String) str); 
          XtVaSetValues(_label, XmNlabelString, xmstr, NULL); 
      } 
      
      void LabeledText::setReadOnly(Boolean readonly) 
      { 
          XtVaSetValues(_textfield, XmNeditable, !readonly, NULL); 
      } 
      

  5. Add entries to the class's interface map data member. The first entry in each line indicates the resource that will appear in RapidApp for the user to set interactively. The second is a string that is the name of the member function. The final argument must be one of XmRString, XmRBoolean, XmRInt, XmRFloat, VkRFilename, VkRNoArg, XmRCallback, or an enumeration, and indicates the type of the single argument supported by the member function associated with this item.

    • Find the section of code in LabeledText.C that looks like:

      static VkCallbackObject::InterfaceMap map[] = {
        // { "resourceName", "setAttribute", XmRString},
        { NULL }, // MUST be NULL terminated
      };
      


    Note: On 5.3 systems, this structure will be declared in the code as simply “InterfaceMap”.


    • Follow the example shown and add each of the member functions to the table:

      static VkCallbackObject::InterfaceMap map[] = {
        {"label", "setLabel", XmRString}, 
        {"readonly", "setReadOnly", XmRBoolean}, 
        { NULL }, // MUST be NULL terminated
      };
      

  6. Save LabeledText.h and LabeledText.C.

  7. Generate the library by selecting “Build Application” from the Project menu.

You can now install the library as described in “Install a Component Library”, and load the library as described in “Loading Components Into RapidApp”. When you restart RapidApp, the Labeled Text component should appear on the appropriate palette. You can now add this component to your interface as you would any other interface element. When you do so, the result should be similar to that shown in Figure 8-4.

Figure 8-4. Using the LabeledText Component


Also, when you have the LabeledText component selected, the RapidApp resource editor should display the resources you added, as shown in Figure 8-5.

Figure 8-5. LabeledText Resources


Arguments and Argument Types

Member functions made available as resources in RapidApp must conform to a few strict requirements. Functions must be public and have a void return type. They must not be virtual, and they must take a single argument. This single argument can be of one of the following types:

  • const char *. You can designate this argument type in the interface map structure using XmRString.

  • Boolean. You can designate this argument type in the interface map structure using XmRBoolean.

  • int. You can designate this argument type in the interface map structure using XmRInt.

  • float. You can designate this argument type in the interface map structure using XmRFloat.

  • A const char * that represents a file name. You can designate this argument type in the interface map structure using VkRFilename on IRIX6.2 or later systems. On earlier systems, use the string “Filename”. RapidApp will provide a file input field, with a drop pocket for this type of resource.

  • An enumeration. You can designate this argument type in the interface map structure using a string of the form:

    
    “Enumeration:Qualifier:Type: VALUE1, VALUE2, VALUE3”
    
    

    Enumeration is a keyword that indicates that this is a description of an enumerated type. This word must be followed by a colon.The next two fields must indicate the type of the enumeration. If this enumeration is declared as part of a class, then the Qualifier field is the name of that class, while the Type is the name of the enumeration. If the enum is not part of a class, the Qualifier field can be blank, or set to the word “Global”

  • Functions may also take no argument, indicated by the symbol VkRNoArg, on IRIX 6.2 or later. On IRIX 5.3 and other earlier systems, use the string “NoArg”. However, member functions registered in this way are not currently used within RapidApp.

Support for Callbacks

Member functions presented as resources in RapidApp, as described above, allow an object to be manipulated from the outside. However, it is also useful for objects to have a form of output, and to be able to communicate that something has changed. The ViewKit library, on which the code generated by RapidApp is based, supports an easy way to add callbacks to C++ classes.

To add a callback to a class, simply define a constant string as a public member of the class. For example:

class MyClass : public VkComponent {
const char *const myCallback;
// ...
};

Then define the string in the source file:

const char *const MyClass::myCallback = “myCallback”;

Once defined, this class can invoke callbacks by simply calling:


callCallback(myCallback, (void *) data);

Other classes can register callbacks using VkAddCallbackMethod(), or VkAddCallbackFunction().

To make this callback available in RapidApp, simply add an entry to the InterfaceMap structure, like this:

static VkCallbackObject::InterfaceMap map[] = {
  {"myCallback", NULL, XmRCallback}, 
  { NULL }, // MUST be NULL terminated
};

The first item is the name of the callback. The second parameter can be NULL if this callback is defined by this class, or it can be set to the class name that defines the string, if the class is different. This might be true if the callback is inherited, for example. The final parameter must be XmRCallback, to indicate that this is a callback.

When this component is loaded by RapidApp, the callback will be made available as a resource, just like callbacks for Motif widgets.

Load Non-RapidApp Components

This section includes the following:

Introduction

In addition to loading components created in RapidApp, you can load other C++ components as long as they follow certain requirements. This section describes those requirements, and provides special instructions for loading components for which you don't have the source code (for example, components in third-party libraries).


Caution: Whether you create classes using RapidApp, or write them by hand, it is important to guard against bugs that could affect the environment in which the component will be executed. For example, any memory corruption problem in a component loaded into RapidApp could ultimately cause RapidApp itself to crash. You should test all components with a test driver and with a memory checker to avoid memory corruption problems. You should then use the Component Tester application provided with RapidApp to test the component, as discussed in “Testing Components”.


Requirements for Loading Components Not Created With RapidApp

There are certain general requirements and guidelines that components must follow for you to use them with RapidApp successfully:

  • They must be based on IRIS ViewKit and IRIS IM. Specifically, the component must be derived from the IRIS ViewKit VkComponent base class, described in Chapter 2, “Components,” of the IRIS ViewKit Programmer's Guide.

  • They should be self-contained and not require complex initialization or other external components as part of their visible interface to operate. It is permissible for a component to contain or create other components as long as they are encapsulated. However, RapidApp has no way to deal with a component that requires an instance of another object to be passed to it, for example.

  • They shouldn't cause side effects outside the component that could affect the environment in which it is instantiated. For example, a component shouldn't call exit() in the event of an error. Because the component is instantiated within the same address space and process as RapidApp, a component that calls exit() in response to invalid input also causes RapidApp to exit. Similarly, components should not install signal handlers, which may interfere with other components in RapidApp's address space, or RapidApp itself.

  • They should be able to be destroyed, and re-instantiated at any time. RapidApp often destroys and re-creates interface elements as part of its manipulations.

  • They should behave properly when resized. Using RapidApp, the user can force a component to have any size or shape. If a component is useful only at a given size or shape, it is less useful to users.

  • They should be able to be instantiated as many times as necessary, including having multiple instances in existence at once.

Additionally, all components that you want to load into RapidApp must follow a particular API. Most of these requirements are simply a restatement of the VkComponent protocol, but there are some features that are RapidApp requirements:

  • Derive all components either directly or indirectly from VkComponent, and follow that class's API, as described in Chapter 2, “Components,” of the IRIS ViewKit Programmer's Guide. In general, avoid multiple inheritance. If you do use multiple inheritance, you must list the VkComponent class first in the base class list.

  • The component must have a constructor with the form:

    Component::Component(const char *name, Widget parent)
    

    If the constructor supports additional arguments, they must have default values and not be required for proper operation of the component.

    Instantiating the component should create any widgets required by the component. The widgets must form a single-rooted subtree. The root of the widget subtree is referred to as the base widget of the component. Components typically use a container widget as the root of the subtree; all other widgets are descendants of this widget. The constructor should manage all widgets except the base widget, which should be left unmanaged.

    See “Component Constructors” in Chapter 2 of the IRIS ViewKit Programmer's Guide for more information on component constructors.

  • Every component should assign the widget that serves as the root of the component's widget hierarchy to the data member _baseWidget, which is inherited from VkComponent. RapidApp expects the baseWidget() member function, which is also inherited from VkComponent, to return this widget.

  • Every component must have a show() and a hide() member function, which may be inherited from VkComponent. show() must make the component visible, while hide() is expected to remove the component from the screen. See “Displaying and Hiding Components” in Chapter 2 of the IRIS ViewKit Programmer's Guide for more information on the show() and hide() member functions.

  • You must provide two static member functions, Create<ClassName>() and Register<ClassName>Resources(), where <ClassName> is the name of the class. Create<ClassName>() creates and displays an instance of a component. Register<ClassName>Resources() registers with RapidApp any component resources that you can set using the RapidApp resource editor. For example, the following code is generated by RapidApp for the Calculator class:

    ///////////////////////////////////////////////////////////////////
    // C-callable creation function, for importing class into rapidapp
    ///////////////////////////////////////////////////////////////////
    VkComponent * Calculator::CreateCalculator( const char *name, Widget parent ) 
    {
        VkComponent *obj =  new Calculator(name, parent);
    
        return (obj);
    }
    
    ///////////////////////////////////////////////////////////////////
    // Function for importing this class into rapidapp
    ///////////////////////////////////////////////////////////////////
    
    void* Calculator::RegisterCalculatorInterface()
    {
        // This structure registers information about this class
        // that allows RapidApp to create and manipulate an instance.
        // Each entry provides a resource name that will appear in the
        // resource manager palette when an instance of this class is
        // selected, the name of the member function as a string,
        // and the type of the single argument to this function.
        // All functions must have the form
        // 
        // void memberFunction(Type);
        //
        // where “Type” is one of:
        // const char * (Use XmRString)
        // Boolean (Use XmRBoolean)
        // int (Use XmRInt)
        // float (Use XmRFloat)
        // No argument (Use VkRNoArg
        // A filename (Use VkRFilename)
        // An enumeration (Use “Enumeration:ClassName:Type:VALUE1, VALUE2, VALUE3”)
        // A callback (Use XmRCallback)
    
        static VkCallbackObject::InterfaceMap map[] = {
          // { "resourceName", "setAttribute", XmRString},
          { NULL }, // MUST be NULL terminated
        };
    
        return map;
    }
    


Note: On systems earlier than IRIX 6.2, the type of the InterfaceMap structure is not defined by the VkCallbackObject class. You can see the correct structure in the Component.5.3 example, distributed with RapidApp.


Testing Components

Before loading a component into RapidApp, you should use the Component Tester application provided with RapidApp to test the component to make sure that there are no unexpected side effects that would affect the operation of RapidApp. To run the Component Tester, enter:

% comptester 

The Component Tester appears as shown in Figure 8-6.

Figure 8-6. The Component Tester


To load a component into the Component Tester, select “Load Component” from the File menu. The Component Tester displays the Load Component dialog shown in Figure 8-7.

Figure 8-7. The Component Tester Load Component Dialog


Specify the class name of the component in the “Class to be Imported” field, and the DSO library in the “Selection” field, then click to OK button to load the component.

The Component Tester displays the components you load and allows you to manipulate them. If your component contains any component resources, the Component Tester displays them as items in an Operations menu. You can then select these menu items to change the resource values.

Completely exercise all features of your component. If there are no problems, you can safely use this component with RapidApp. If there are problems, correct them before trying to use the component with RapidApp. Source code for the Component Tester is included in /usr/share/src/RapidApp/ComponentTester/. You can compile the application using the debug libraries if you need further information while debugging your component.

Adding Custom Base Classes to RapidApp

When you declare a widget or collection of widgets as a class, you are offered a choice of base classes from which to derive the new class. If the user interface element is a VkWindow or SimpleWindow, you are offered the corresponding ViewKit window classes. Otherwise, the choices are VkComponent, or VkMsgComponent, which are also ViewKit classes.

In some cases, you may wish to provide your own classes from which to derived components created with RapidApp. You can do so, subject to the following restrictions:

  • The class must be a subclass, direct or indirect, of VkComponent, VkWindow, or VkSimpleWindow.

  • Only window classes can be derived from VkWIndow or VkSimpleWindow, or user-added subclasses. All other classes must derived from VkComponent, or a user-added subclass of VkComponent.

  • Your class must not have any user interface.

  • The class must present the same derived class protocol as VkComponent, (or in the case of window classes, VkWindow or VkSimpleWindow).

The most common use of this feature is to create a class that supports some additional programmatic protocol that you would alike other classes to inherit.

An Example Base Class

For example, consider a new class, ImageComponent, that adds a public member function to VkComponent, void loadImageFile(). The class could be declared as follows:

#include <Vk/VkComponent.h>

class ImageComponent : public VkComponent {
  public:
    void loadImageFile(char *filename);
};

To allow this class to be chosen as the base class of new components created in RapidApp, the class must be registered with RapidApp. This requires the following steps:

  1. Create a file with the same name as your class. In the above example, the file would be named ImageComponent.

  2. Add lines to this file to describe the header file and location for this class and also the library in which this class can be found. The format of this file is the same as an X resource file. For example, if the declaration of this class is found in a file ImageComponent.h, which is normally located in /usr/include/Image, and the class is in a library named libimage.so, you could write this file as follows:

    ! RapidApp registration file for the ImageComponent class
    *ImageComponent*headerFile: <Image/ImageComponent.h>
    *ImageComponent*library: -limage
    
    

  3. Install this file in one of two locations.

  • If you are just testing, or using this class for your own personal use, you can place this file in

    
     $HOME/.rapidappdir/BaseClasses/VkComponent/ImageComponent.
    
    

  • If you wish to share this class with others, or make it a more permanent part of your development environment, you can install the file in

    
     /usr/lib/RapidApp/BaseClasses/VkComponent/ImageComponent
    

You can describe new subclasses of VkWindow or VkSimpleWindow to RapidApp in the same way. The restrictions are the same, as are the installation steps. However, subclasses of VkWindow must be placed in $HOME/.rapidappdir/BaseClasses/VkWindow, or /usr/lib/RapidApp/BaseClasses/VkWindow

Subclasses of VkSimpleWindow must be placed in $HOME/.rapidappdir/BaseClasses/VkSimpleWindow, or /usr/lib/RapidApp/BaseClasses/VkSimpleWindow.