Jump to content

The Open Objects Library (OOL) - Course of Instruction

From EDM2
Revision as of 22:43, 3 December 2015 by Martini (talk | contribs) (First Steps)

Introduction

What is the OOL?

The Open Objects Library (OOL) is a C++ library wich contains classes for the GUI, datatypes, multimedia, TCP/IP and more. To use OOL it is an advantage if you have some experiance in programing using class-librarys and/or OS/2-API functions.

Very nice tests of the OOL were presented in "c´t magazin" 10/96 and "Software Entwicklung" 11/96.

Currently only the most modern operating system (OS/2 Warp 3 or higher) is supported.

First Steps

Creating a basic application

In some class librarys (and in earlier versions of the OOL) it is nessacary to create an instanance of an application class. This application class usualy represents the current process and some service functions.

In this version of the OOL an application object is created automaticaly when the function main() of your application is reached. This is an instance of XApplication wich can be accessed with the static function XApplication::GetApplication(). E.g. if you need a pointer to the application object you will code:

int main()
{
    //now we get a pointer to the
    //application object
    XApplication * currentApp = XApplication::GetApplication();
}

You need this pointer allways to make the application work, it does not start itself because you must create a window or dialog (if you use windows or dialogs in your application) before the application starts to work. Having this things in mind a simple main-method looks like:

int main()
{
    //we get a pointer to the application object
    XApplication * currentApp = XApplication::GetApplication();

    //here a window is created
    myWindow * w(...);

    //now start the application
    currentApp->Start();
}

Creating the first frame window

Usualy the first thing you want to do is to create a main window. The following source describes how to create an instance of the frame window class XFrameWindow:

   XFrameWindow * myWindow = new XFrameWindow( /* parameters */ );

This part of code produces a frame window, but is not very useful: To handle events etc. it is nessecary to overwrite some functions of the class XFrameWindow. Therfore you have to derive a class of XFrameWindow (the following code is avaible in the directory "hello1"):

class MyWindow: public XFrameWindow
{
    public:
        void Draw();
        BOOL DoCommand(LONG);
        MyWindow();
};

Here two methods are overwriten and a constructor for our new class is declared.

At first we have to create the construtor. In this case we create a simple window which has no menus or other controls except a little text, we give the window the window-id 100, the title should be "Hello World" and the style of the window should be the default frame window style.

MyWindow :: MyWindow( ): XFrameWindow( 100 , "Hello world!", XFrameWindow::defaultStyle)
{
    //at first we define a color which is used to paint the background
    //we chose white as background color
    XColor color(COL_WHITE);
    SetBackgroundColor( &color );

    //create a text
    //the owner of the text is this window, show the text "Hello World"
    XStaticText * text = new XStaticText( this,
                                          "Hello world!",
                                          XRect( 100, 100, 150, 20));

    //Set the position of this window
    //left-lower corner is 100,100, width is 300, height is 200
    XRect rect(100,100,300,200);
    SetSize(&rect);

    //make this window the active window
    Activate();
}

Now the overwritten methods must be implementated. The method Draw() is overwritten so we can draw the content of the window, the implementation of this methods is very simple:

void MyWindow :: Draw()
{
    //just fill the background, the text will draw itself
    FillBackground();
}

The method DoCommand() allows us to receive commands which are send from menus or toolbars, in this first sample we create this method, but dont use it, but we must return a value! We return FALSE to show the library that we have not handled the command.

BOOL MyWindow :: DoCommand(LONG com)
{
     //at this point we dont care about commands
    return FALSE;
}

At least we need a main-method in which our window is created and the application starts to work:

int main()
{
    //we get a pointer to the application object
    XApplication * currentApp = XApplication::GetApplication();

    //here a window is created
    MyWindow * myWin = new MyWindow();

    //no start the application
    currentApp->Start();
}

Error handling

Usualy errors apearing while your program is runing are shown by the return value of a member function, eg. a function returns FALSE if a method was not succesful or an errocode is returned, eg. if a file could be opened a method returns zero, if the file couldn't be opened the errorcode of the operating system is returned.

If a very serious error ocures, the OOL throws exceptions of the type XException (and derived classes), you can catch them in the usual manner:

try

{
    MyWindow * window = new MyWindow( /*parameters*/ );
    // do something with the window
}
catch( XException& exception)
{
    // show the error
    exception.ShowError();
}

More about frame windows

If you have compiled the last example and tested it, you may have seen that the text "Hello world" is not moved if the size of the frame window changes. Perhaps you want to display the text centered, in this case we need to handle the sizing of the frame window. At first we have to overwrite the method XFrameWindow::DoSize() (the code is avaible in the directory "hello2")

class MyWindow: public XFrameWindow

{

    public:
        ......
        void DoSize(XSize *);
};

and create an implementation:

void MyWindow :: DoSize(XSize * size )
{
}

As you can see we have a little problem: we dont have a pointer to the text-object we created in the constructor of the frame window! There are two methods to solve this problem:

1.- Store the pointer

The simple way is to store the pointer to the object in the class MyWindow, simply declare a member like

    class MyWindow: public XFrameWindow
    {
        private:
            XStaticText * text;
        public:
            //methods
    };

Once created you access the text object about this pointer.

This method is simple and fast, but expensive: for ever window created on the frame window 4 bytes are used.

2.- Use window-IDs

The second method is to give unique IDs for the child windows. Each window gets an ID in its constructor like

   XStaticText * text = new XStaticText( this,
                                         "Hello world!",
                                         XRect(),
                                         TX_CENTER,
                                         300);

(here the ID is 300). If the window is created you can access it with the member function GetWindow(), eg.

   XWindow * text = GetWindow(300);

or, if you have to make a typecast

   XStaticText * text = (XStaticText*) GetWindow(300);

This method is not so expensive like the first one but is a little bit slower (I prefer the second).

Warning: if a window does not exist with the requested ID GetWindow() returns NULL!

Asuming we chose the second method, the constructor of the text object must be changed to:

XStaticText * text = new XStaticText( this,
                                      "Hello world!",
                                      XRect( 100, 100, 150, 20),
                                      TX_CENTER,
                                      300);

The method DoSize() of the frame window looks like:

void MyWindow :: DoSize(XSize * size )
{
    XWindow * text = GetWindow(300);

    if(text) //is the text object created?
    {
        //calculate the new size/position of the text object
        XRect newSize;
        newSize.SetX( size->GetWidth() / 2 - 75 );
        newSize.SetY( size->GetHeight() / 2 - 10 );
        newSize.SetWidth( 150 );
        newSize.SetHeight( 20 );
        text->SetSize( &newSize );
    }
}

You may have noticed above that windows are allways created with the operator new(). This is nessacary because the destructor of a window-object is called automaticaly when the window is closed. The destructors of all child windows, menus and toolbars are called also. If you would code in the main()-function

int main()
{
    //....
    MyWindow( /* parameters */ );
    //....
}

the destructor of myWindow would be called two times: first time when the user close the window, second time when the main()-function will be left, an error ocures!

The same effect is reached if you create child windows in the constructor of the frame window like:

class myWindow: public XFrameWindow
{
    XEntryField entryField;
    public:
    //methods
};

MyWindow::MyWindow(): XFrameWindow(/* parameters */), entryField(/* parameters */)
{
    //other code
}

Here the destructor of entryField would be called two times too:

  1. The destructor of entryField is called automaticaly from the OOL when MyWindow is cloesed
  2. When MyWindow is destructed the destructor of entryField is called

Another error related to this problem:

MyWindow::MyWindow(): XFrameWindow( /* parameters */)
{
    XEntryField entryField(/* parameters */);
}

Here the destructor is called when the constructor of MyWindow is left!

To beware yourself from the described problems allways use new(). Like described above you may store the pointer to created child windows in your frame window class or you can use the method GetWindow() to access child windows. Windows with menus and toolbars You usualy want to give the user the chance to select some actions to be executed by your application. One method is to create a menubar for your frame window. At the first step you create a menu in the program resource file (*.rc, see Toolkit-documentation for details). In the source code you can load this menu with two methods (the following code is avaible in directory "menu1"):

You give the frame window the same ID like the menubar and specify the style FRM_MENU which shows the OS that a menubar should be created.

This way is very simple:

    //we asume that the menubar has the ID 200
    MyWindow::MyWindow(): XFrameWindow( 200,
                                        "Hello world!",
                                        FRM_MENU | XFrameWindow::defaultStyle )
    {
    }

You may want to load the menubar dynamicaly. This method has the advantage that you can chose at runtime which menu to load (e.g. if you have menubars for multiple languages). In this case you code:

    //we asume that the menubar has the ID 200
    MyWindow::MyWindow(): XFrameWindow( 0, //ID is not nessacary
                                        "Hello world!",
                                        XFrameWindow::defaultStyle )
    {
        XMenuBar * menu = new XMenuBar(this, 200);
    }

(remember that the destructors of the menubar are called automaticaly). Independent of which way you created the menubar you need a method where you can handle the commands send from the menubar when the user selected an item of the menu. Like described above you have to overwrite the method DoCommand():

class MyWindow: public XFrameWindow
{
    public:
    //methods
    BOOL DoCommand(LONG command);
};

The method DoCommand() allows you to handle the commands, the parameter command has the ID of the selected menu item. Usualy you use symbolic names for the menu IDs, like

#define IDM_MAIN      200  //the ID of the main menu
#define IDM_FILE      210  //ID of the submenu "file"
#define IDM_FILE_OPEN 211  //ID of the menu item "file-open"
//and so on

Done so the commands can now be handled. Usualy you create a "switch()"-block to determine which command was send:

BOOL MyWindow :: DoCommand(LONG command)
{
    switch( command )
    {
        case IDM_FILE_OPEN:
            //handle the command to open a file
            break;
        case IDM_FILE_CLOSE:
            //handle the command to close a file
            break;
        default:
            return FALSE;   //show the library that we did not handle the command
    }
    return TRUE; //show the library that we have handled the command
}

Another way to allow the user to make some choices is to create a toolbar.

   XToolBar * toolBar = new XToolBar( this );

Now you can add control-windows like buttons, combo-boxes etc. to this toolbar. If you add push-buttons to the toolbar every time this button is pressed a command with the ID of that button is send to the method DoCommand() of the frame window the toolbar belongs to. From this reason you must make shure to give the buttons unique IDs (it is a good idea to give a button the ID of the corresponding menu item).

Adding controls to toolbars is done with two steps: the first is to create the control with the the toolbar as parent, the second step is to add the control with the toolbars method AddWindow():

MyWindow::MyWindow():XFrameWindow(/* parameters */)
{
    XToolBar * toolBar = new XToolBar( this );
    XPushButton * button = new XPushButton( toolbar,
                                         XRect( 0,0,40,20),
                                         IDM_FILE_OPEN,
                                         WIN_VISIBLE,
                                         "Test");
    toolbar->AddWindow( button );
}

More about windows

    • Event Handling
      • Events vs. Commands
      • The concept of handling events in the OOL
      • Handling simple events
      • Handling complex events
    • Creating frame windows from resources
    • Dialogs
      • Dialogs vs. frame windows
      • Modal
      • Modeless
    • More about control windows
      • Notebooks
      • Container controls
        • Creating the container
        • Init
        • Inserting columns (detailed view only)
        • Adding objects
        • Handling container events
        • Retrieving container objects

Files and information about files

    • Open a file
    • Read/Write from/to a file
    • Information about files
    • Finding files

Process classes and process-communication

    • Threads
    • Semaphores
    • Pipes
      • Named
      • Unnamed
      • Named
    • Sockets
    • DDE

Enhanced topics

Multimedia

The OOL contains two classes for basic multimedia support:

  • XSound is a class to play WAV and MIDI files
  • XVideo can play digital videos (depending of which CODECs are installed)
  • XCDPlayer

For all classes MMPM/2 must be installed (on Warp 4 it is always installed).

To use the multimedia classes (see directory "sound" for sourcecode):

  • create an instance of the needed class
  • load a file
  • use the methods Play(), Stop() etc

The methods to play a file are usualy derived from the class XMediaWindow, this methods send a notification code to the owning frame window when the action has finished, this notification codes can be caught in the method DoControl() of the frame window. E.g. if you have started to play a video in the DoCommand() method you recive an object of the type XControlEvent which has the ID MEDIA_PLAYED.

You must make shure that a created multimedia-object is destroyed before your application is finished!

Using the OOL and other class librarys

Objects of the OOL are tested with the Standard Template Library (STL). To use the data-objects XString, XDate and XTime with the STL the operators "<" and "==" and the copy-constructors are overwriten in these classes, you shouldnt have any problems to use this classes with sorted containers like set or bag (never, never put a window-object in a container!)