GEOS-SC : Tutorial : Sections : Creating a Simple Application
Introduction | The Development Environment | Using an Existing Sample Application | Creating a Simple Application

GEOS-SC consists of one program, GeosSC.exe. It is made up of pieces of code called modules. Each module is an application, library, or driver. We will show how to create a new application module.

For this exercise, we will create an application that uses a button to display the phrase "Hello World!" We'll explain the functionality each application is responsible for. We'll see how to create a simple user interface.
[screen image of application]

At each step of this tutorial, you see a set of links like this: (header | source | resource). They link to the complete source code for the application: its header (.h) file, main source code (.cpp) file, and resource (.str) file. (The resource file holds user-visible strings. We'll discuss this later.)

To create your own sample application:

  1. Create the tutsamp module.
  2. Fill in the file tutsamp.str with user-visible strings.
  3. Fill in the file tutsamp.h with the code that represents the application to the system.
  4. In tutsamp.h: define the resident application.
  5. Fill in the file tutsamp.cpp with the body of the application's source code.
  6. In tutsamp.cpp: handle start-up and shutdown.
  7. In tutsamp.cpp: define BuildUI(), a helper function to set up the user interface objects.
  8. In tutsamp.cpp: continue writing BuildUI().
  9. In tutsamp.cpp: define ActionPerformed(), a member function to handle button presses.
  10. Build and execute the executable.

  Create the tutsampp module.

If you are not already in the GEOS-SC Workspace, enter it now: Choose GEOS-SC workspace from the GEOS_SC SDK program group in the Start menu. (You can also open this workspace in Microsoft Visual C++ by choosing Open Workspace from the File menu and selecting GeosSC.dsw.)

[Start menu screen image]

To create a new application

  1. Create a directory for the application. If you installed the SDK to the directory C:\geos-sc\, then create the directory under C:\geos-sc\appl\. Call the directory tutsamp.
  2. C:\geos-sc\appl\>mkdir mystuff
    
    C:\geos-sc\appl\>mkdir mystuff\tutsamp
  3. Use Microsoft Visual C++ to create three new files (tutsamp.cpp, tutsamp.h, and tutsamp.str) in this directory.
  4. First, create the C++ source file tutsamp.cpp. Select "New" from the "File" menu.

    In the dialog box, select "C++ Source File", enter the File name "tutsamp.cpp", and specify the path to the tutsamp directory in the Location field (in this example, the path is "C:\GEOS-SC\appl\mystuff\tutsamp"). Click "OK".

    Repeat these steps to create tutsamp.h, and tutsamp.str. Be sure to specify that these are "C/C++ Header File"s, not "C++ Source File"s. When you are done, these files will appear under Geos-SC files in the file view, as shown in step 4.

  5. Run the mkjam program in the new directory. This creates a file called Jamfile, which the compilation tools use. Each module has a Jamfile.
  6. C:\geos-sc\appl\mystuff\tutsamp>mkjam
    Jamfile created.
    No toolchain specified, using "Microsoft32"
    No configuration specified, using "ErrorCheck"
    Using NT.jam
    Using Microsoft32.jam
    Using (optional) GeosSc.jam
    ...found 12 target(s)...
    ...updating 2 target(s)...
    MakeDspFile ..\..\..\appl\mystuff\tutsamp\tutsamp.dsp
    ...updated 2 target(s)...
    C:\geos-sc\appl\mystuff\tutsamp>
  7. In Microsoft Visual C++, open the GEOS-SC top-level Jamfile.
  8. Add a Module line to the top-level Jamfile. The contents of this line should reflect the application's file path. Place the new line with the other Module lines:
  9. ...
    Module appl samples shell envshell ;
    Module appl samples internet netman ;
    Module appl browser ;
    Module appl mystuff tutsamp ;
    Module appl javalaunch : Inactive ;
    ...
    Spaces are important, including the space before the semicolon.

  Fill in the file tutsamp.str with user-visible strings. (resource | header | source)

To open the tutsamp.str file, double-click its entry in the FileView section of the Workspace view (under the GeosSC Files heading).

[Screen image of FileView]

Replace the contents of this file with the text found at the (resource) link.
 
.prefix TUTSAMPAPP

FRAME_TITLE     "Sample"
BUTTON_LABEL    "Click Here"
LABEL_TEXT      "Hello World!"

The .str file enables your module to support localization. We will use this file to define all user-visible strings. You may have at most one .str file for each module. When you build your application, the build tool will generate two files from your .str file: resource.h and resource.cpp. (If you're curious, you can look in these files to see what they do.)

This application has three user-visible strings. To allow this, we set up the .str file with a .prefix directive, then define names and values for the three strings.

To use these strings, the application's C++ code must

You may wonder what the TCHAR type is. This type represents characters. ASCII symbols require one byte each. Other languages, notably Asian languages, have so many symbols that you need two bytes per character. The TCHAR type is one byte on Single-Byte Character Set (SBCS) devices and two bytes on Double-Byte Character Set (DBCS) devices.

On Double-Byte Character Set systems, the contents of the .str file should be Shift-JIS (a superset of ASCII).


  Fill in the file tutsamp.h with the code that represents the application to the system. (resource | header | source)

Again using the FileView tab, open the tutsamp.h file. Replace its contents with the text found at the (header) link.

Let's look at the first part of tutsamp.h:
 
#ifndef _TUTSAMPAPP_H_
#define _TUTSAMPAPP_H_

#include <toolkit/uifact.h>         //for UI objects (FlexFrame, etc.)
#include <ui/appbase.h>             //for AppBase class
#include <toolkit/lisact.h>         //for ActionListenerInterface class
#include <resapp/resapp.h>          //for ResidentApplication class

class TutorialApp : public ActionListenerInterface, public AppBase 
{
public:
    TutorialApp(void);

    // SetAppContext is overridden to build the UI.
    virtual void SetAppContext(const TCHAR *context);

    // GetAppContext is overridden to save the application state.
    virtual TCHAR *GetAppContext(void);

    // ActionPerformed is overridden to intercept button presses.
    virtual void ActionPerformed(ActionEvent& event);

    // Exit is overridden to clean up when the application quits.
    virtual void Exit(void);

private:

    // Helper function to create our UI.
    Result BuildUI(void);

    // Flag to let us know if we have already built our UI.
    Boolean         _builtUI;

    // This object displays the "Hello World!" string.
    // We keep its pointer handy so that code can
    // reference it to make it appear and disappear.
    FlexLabel       *_helloworldLabel;

    // We keep a pointer to this object 
    // so that we can delete it 
    // when shutting down the application's UI.
    VerticalFlowLayout  *_layout;
};

We need to define two classes which will represent our application to the system:
Application Base
The system tells the Application Base object when the application is being loaded and unloaded.
Resident Application
Even if the rest of the application hasn't been loaded into memory, the Resident Application will be loaded. The Resident Application tells the system the application's name and can load the rest of the application.
First, we define our Application Base class. When the user runs the application, an object of this class will be created. This object must define certain member functions which the system calls.

The system notifies the Application Base object of important events, including

To fulfill these responsibilities, our Application Base object inherits from AppBase and overrides the SetAppContext(), GetAppContext(), and Exit() member functions. Later, we'll see how our application defines these member functions.

Because we know that the application base object is in memory when the application is running, it's typical to use this object to keep track of resources associated with that object. We also use the application's class to define variables within the application's "scope" (and avoid cluttering the global name-space). In this example, we'll use the application base object member variables to store some values which will be useful throughout the application's life cycle (_builtUI, _helloworldLabel, and _layout).

The application base object is a handy place to define miscellaneous application functionality. This application doesn't have much functionality--it just responds to button presses. We let the application respond to these button presses by making it inherit from ActionListenerInterface and overriding ActionPerformed(). Later, we'll see how to make this object "listen" to a button.


  In tutsamp.h: define the resident application. (resource | header | source)

Let's look at the rest of the code in tutsamp.h:
 
// Strings that are not user-visible
const TCHAR *const TUTSAMP_APP_NAME = _TEXT("tutsamp");

// Subclass of ResidentApplication to register the app with the system.
class TutorialSampResidentApplication : public ResidentApplication 
{
public:
        TutorialSampResidentApplication() : ResidentApplication(TUTSAMP_APP_NAME) 
        {};
        virtual AppBase *CreateAppBase(void) 
        {
            return new TutorialApp;
        }
};

static TutorialSampResidentApplication tutsampApp;

#endif  // _TUTSAMPAPP_H_

The resident application object supplies information to the system even when the application isn't running. The resident application object must inherit from ResidentApplication. The constructor for the resident application object registers the application with the system. This tells the Program Manager (envshell) to add this application to its list of available applications. The system identifies the application by the string passed to the resident application object's constructor. Our application name string must be unique on the device; otherwise, the system will get our application confused with other applications.

When the system wants the application to run, it calls CreateAppBase(). Our resident application responds by creating an object of the application's base class.

We use the _TEXT() macro to define a string constant. Like TCHAR, _TEXT() makes your code adaptable to different character sets. It returns a TCHAR string.


  Fill in the file tutsamp.cpp with the body of the application's code. (resource | header | source)

Again using the FileView tab, open the file tutsamp.cpp. Replace its contents with the text at the (source) link.

Let's look at the first part of this code.
 
#include <system.h>                 //for standard GEOS-SC types
#include <toolkit/uifact.h>         //for UI factory
#include <flexui/javaspui.h>        //for UI hints
#include <tstring.h>                //for TCHAR string functions (Tstrcpy)
#include "tutsamp.h"                //for our class definition
#include "resource.h"               //for our user-visible strings

// Sets the name the system uses for the application static
AppNameAttribute tutsampAppName(&tutsampApp, TUTSAMPAPP_FRAME_TITLE);

TutorialApp::TutorialApp() :
    _builtUI( FALSE ),
    _layout( NULL )
{};

Remember that resource.h is automatically generated from the .str file and contains the constant strings needed in the application. We #include that file here.

We create an AppNameAttribute object, attaching it (by means of its constructor) to the resident application object. This attribute object maintains the human-readable string by which the Program Manager (envshell) can identify the application to a user. We use the string TUTSAMPAPP_FRAME_TITLE, which we defined in the .str file. We define a constructor for the class to initialize its member data.


  In tutsamp.cpp: handle start-up and shutdown. (resource | header | source)

Let's look at the next piece of the source file:
 
// Updates the application's state, building the UI if necessary.
/* virtual */ void
TutorialApp::SetAppContext(const TCHAR *context)
{
    // Build the application's UI if we haven't already.
    if ((!_builtUI) && BuildUI() != SUCCESS) {
        EC_WARN("Could not create UI.");
        Exit();
    } 

    // If there's a context string, parse it and update the UI.
    if (context) {
        if (Tstrcmp( context, _TEXT("+")) == 0) {
            _helloworldLabel->SetVisible(TRUE);
            return;
        } 
        if (Tstrcmp( context, _TEXT("-")) == 0) {
            _helloworldLabel->SetVisible(FALSE);
            return;
        } 
        EC_WARN("Unrecognized context string");
    }
} // End of TutorialApp::SetAppContext() 

// Encodes the application's state into a string.
/* virtual */ TCHAR *
TutorialApp::GetAppContext(void)
{   
    if (_builtUI != TRUE ) {
        return NULL;
    }
    TCHAR scratch[30];
    if (_helloworldLabel->IsVisible() == TRUE) {
        Tstrcpy( scratch, _TEXT("+"));
    } else {
        Tstrcpy( scratch, _TEXT("-"));
    }

    TCHAR *context = new TCHAR[Tstrlen( scratch ) + 1 ];
    if ( context == NULL ) {
        EC_WARN("Could not allocate context string; will not save context.");
        return NULL;
    }
    Tstrcpy( context, scratch );
    return context;     

} // End of TutorialApp::GetAppContext

// The application is closing; it's time to clean up.
/* virtual */ void
TutorialApp::Exit(void) 
{
    delete _layout;

    // Call inherited member function to get default behavior.
    AppBase::Exit();
} // End of TutorialApp::Exit()

Remember that the system alerts the application base object when the application starts or exits; the system also asks the application to save state information and to restore itself from state.

Subclassed AppBase member functions handle these cases:

Because you need to handle all the cases described above, you need to perform certain tasks in these member functions:
SetAppContext()
Should initialize UI and set up if it hasn't done so already. If a non-NULL context argument was passed, update the application's state appropriately.
GetAppContext()
Should examine the application's state and generate a context string encoding the state. The system (or whatever called GetAppContext()) is responsible for freeing the context string.
Exit()
Should clean up, freeing resources.
A context string can be whatever you like. Our application's UI has only two states: either it's showing "Hello World!" or it isn't. It uses only two state strings: "+" and "-".

Since we are using strings of TCHARs, we use the special string functions Tstrcmp(), Tstrcpy(), and Tstrlen() defined in tstring.h. Use these functions as you would use the analogous standard ANSI C string functions, but prepend "T" to the function name.


  In tutsamp.cpp: define BuildUI(), a utility function to set up the user interface objects. (resource | header | source)

Our SetAppContext() function uses a utility function, BuildUI(), to build the application's user interface. We define the body of that function here. This function creates the objects which form the user interface of the application. First, it creates the frame: a screen or window to which it can add the other objects later. Let's look at how the function sets up this object.
 
Result
TutorialApp::BuildUI(void)
{
    FlexFrame *frame = 
        theUIFactory->CreateFlexFrame( HINT_FRAME_WITH_NO_CLOSE_BUTTON, 
                                       TUTSAMPAPP_FRAME_TITLE );
    if (frame == NULL) {
        EC_WARN("Couldn't create frame.");
        return FAILURE;
    }
    if (Add(frame) != SUCCESS) {
        EC_WARN("Couldn't add frame to the AppBase.");
        delete frame;
        return FAILURE;
    }
...
 
[FlexFrame] 

To create the frame, we use the global theUIFactory object. This object is responsible for creating UI objects. We call its CreateFlexFrame() member function, passing two arguments: a lookHint and a title for the frame. The lookHint, HINT_FRAME_WITH_NO_CLOSE_BUTTON, specifies which of the device's standard "looks" this frame uses. The looks for the Java UI pseudo-device are defined in <flexui/javaspui.h>.

You may wonder why we didn't just create the frame with new FlexFrame. Our frame isn't exactly a FlexFrame. It's of a subclass of FlexFrame, a subclass defined by the device's specific UI, a subclass with specialized behavior. (You might end up with a different subclass depending on the lookHint you specify.) We'll interact with this object as if it were a FlexFrame.

The TUTSAMPAPP_FRAME_TITLE string comes from our .str file.

You should always check for failed memory allocations. We check to make sure that the frame was created successfully. If it wasn't, we stop creating UI and return FAILURE. We also use the EC_WARN() macro to report the problem. EC_WARN() is the GEOS-SC equivalent of "printf() debugging"--it prints its message to the DOS shell. You can learn about benefits and details of EC (Error Checking) code in Coding Conventions: Error Checking Code. A list of EC macros is located in the API reference for the sys_info library.

When we've successfully created a frame, we want to add it to the application base object's UI tree. This tree is a hierarchy of UI objects which keeps track of which objects are contained by other objects. When one UI object is contained within another, we say that one is the child and the other is the parent. For example, if a button appears in a dialog box, we say the button is a child of the dialog box. (Only UI objects of classes that inherit from FlexContainer can contain other objects.) To add a UI object as a child of another UI object, use the parent object's Add() member function. You should always check the return value of any OS function that can fail. Here, we check the return value of the application base object's Add() to make sure that the frame was added successfully.

If something went wrong adding the frame to the application base object, we delete the frame. We need to delete the frame but not the application base object because when the application exits, it automatically deletes itself and any objects in its UI tree. We need to delete the frame because it never became part of the UI tree.


  In tutsamp.cpp: Continue writing BuildUI(). (resource | header | source)

Let's look at the rest of the BuildUI() function definition.
 
...
    _layout = new VerticalFlowLayout;
    if ( _layout == NULL ) {
        EC_WARN("Couldn't allocate frame's layout");
        return FAILURE;
    }
    frame->SetLayout(_layout);

    FlexButton *button = theUIFactory->CreateFlexButton(
                                         HINT_BUTTON_JAVA_LOOK_AND_FEEL);
    if (button == NULL) {
        EC_WARN("Couldn't create button.");
        return FAILURE;
    }
    if ( frame->Add(button) != SUCCESS ) {
        EC_WARN("Couldn't add button to the frame.");
        delete button;
        return FAILURE;
    }
    button->SetLabelString(TUTSAMPAPP_BUTTON_LABEL);
    if ( button->AddActionListener(*this) != SUCCESS ) {
        EC_WARN("Couldn't add action listener to the button.");
        return FAILURE;
    }

    _helloworldLabel = theUIFactory->CreateFlexLabel();
    if ( _helloworldLabel == NULL ) { 
        EC_WARN("Couldn't create label.");
        return FAILURE;
    }
    if ( frame->Add(_helloworldLabel) != SUCCESS ) {
        EC_WARN("Couldn't add label to the frame.");
        delete _helloworldLabel;
        return FAILURE;
    }
    _helloworldLabel->SetText(TUTSAMPAPP_LABEL_TEXT);
    _helloworldLabel->SetVisible( FALSE );

    frame->SetVisible(TRUE);

    _builtUI = TRUE;
    return SUCCESS;

}       // End of TutorialApp::BuildUI() 

As when setting up the frame, the rest of the BuildUI() function creates more objects and uses Add() to organize them into a tree.

We create a VerticalFlowLayout object and attach it to the frame. This causes the frame to arrange its children one above the other. If we'd used another kind of layout object, the frame would arrange its children differently.
 
[FlexButton] 

This layout object won't be deleted automatically when the application exits. We keep a pointer to it so that we can delete it in our application base object's Exit() member function.

After creating a button, we use its SetLabelString() member function to set the string it will display, "Click Here". We use its AddActionListener() member function so that the button will tell the application base object when the user presses it. Our application can act as a button's listener because it inherits from ActionListenerInterface. (Later, we'll see how the application base object handles button presses.)
 
[FlexLabel] 

The object which displays the "Hello World!" text is a label. It is of a subclass of FlexLabel. We create the label and add it to the frame in much the same way we did the button. We set the label's text with the SetText() member function. Because we don't want the label to show when the application first starts up, we use the SetVisible() member function to hide the label.

(To learn more about the user interface model, see the Flex UI chapter.)


  In tutsamp.cpp: Write an ActionPerformed() function to handle button presses. (resource | header | source)
 
/* virtual */ void
TutorialApp::ActionPerformed(ActionEvent& event)
{
    USE_IT( event );

    if (_helloworldLabel->IsVisible()) {
        _helloworldLabel->SetVisible(FALSE);
    } else {
        _helloworldLabel->SetVisible(TRUE);
    }
} // End of TutorialApp::ActionPerformed() 

To define the application's response to a button press, we override the ActionPerformed() member function. Whenever the user presses the button, the button will call this function.

This function takes an argument event, with information about the button press. We don't need this information, and ignore this argument. The C++ compiler would generate warnings about unused arguments, but we avoid this by using the USE_IT() macro. This macro doesn't do anything, it just avoids the warning.


  Build ([Build Tool]) and execute the executable ([Execute Tool]).

If the Build tab of the Output window reports "Error executing jam" then there was a compile or link error. Look for notification of errors and warnings.

If some other application appears in the GEOS-SC emulator display window, we need to bring our application to the top. If the display window is showing another application, make the display window active, press the F1 key, and choose Sample in the dialog box that appears, and press the Switch To button.

[Screen Image]

Click the "Click Here" button to make the "Hello World!" label visible.

[Screen Image]

To use the state-saving code, do the following:

  1. With the GEOS-SC display window active, press the F1 key.
  2. A dialog box appears.

  3. Choose "Sample" from the list. Press the "Unload" button.
  4. Behind the dialog box, our application disappears. It has exited.

  5. Press the "Switch To" button.
  6. Behind the dialog box, our application reappears. It has started up, and restored itself.

  7. To see the application, press the dialog box's "Close" button.
Congratulations for creating this application!


Introduction | The Development Environment | Using an Existing Sample Application | Creating a Simple Application