Kroni's Classes User's Guide

By Wolfgang Kronberg

A class library for lazy PM programmers

About this Guide
Software requirements:
 * OS/2 Warp and Visual Age C++.

Hardware Requirements:
 * none (anything which runs Warp and VAC++ is good enough).

Copyright Notice
Kroni's Classes have been re-licensed by it's author under the BSD 3 Clauses license.

Disclaimers
I release this software "as is". Use it completely at your own risk. I can not be hold responsible for any damage which is inflicted directly or indirectly to you or other persons or objects in any way by bugs or unexpected behaviour of Kroni's Classes. If you do not accept this, you are not allowed to use the software at all. If these conditions are illegal in your country, it is not allowed to use the software in your country at all.

I wrote this software completely on my own. However, in some countries, even simple algorithms might be copyrighted. This means that in some countries I might not have the right to grant you the above rights. If this is indeed the case in your country, it is not allowed to use the software in your country at all.

Contacting the Author

 * Wolfgang Kronberg

Where to get Kroni's Classes
You can download Koni's Classes from the EDM/2 wiki page: Kroni's Classes

Introduction Compiling and linking your programs using the IBM Workframe
You can compile and link the programs that use Kroni's Classes just like any other program, with the following exceptions:


 * Since Kroni's Classes are based on the UICL, you must activate multithreading, e.g. on Options/Compile/Object/Multithread.


 * Since your programs need the header files from Kroni's Classes, you must add a path to them (x:\...\KrClass\include) to your include path, e.g. on Options/Compile/File/Options/Command line include path.


 * Since the Linker must be able to find the library (in x:\...\KrClass\lib), you must tell it where to look for it, e.g. by adding the full name (e.g. x:\...\KrClass\lib\krclas.lib) to Options/Link/Filenames/Libraries to use.


 * Always use the correct version of the library! Depending on whether you use dynamic linkage and/or debugging information, the library must be recompiled using different options. For your convenience, the installation program creates libraries for all cases and gives them these names:
 * krclas.lib  (dynamic linkage, no debuging information)
 * krclasd.lib (dynamic linkage, with debuging information)
 * skrclas.lib (static linkage, no debuging information)
 * skrclasd.lib (static linkage, with debuging information)

Of course, all other settings must still make sense, e.g. you must link your program as a PM program.

Please remember that linkage and debugging settings have vast influence on the size of the generated code. When you write programs for yourself, you should use dynamic linkage. Even when you distribute your programs, you should consider dynamic linkage. See "Managing Libraries", "Packaging the Visual Age C++ Runtime Libraries" in the Visual Age C++ User's Guide and Reference for more information.

Also, consider using a make file like the one in the demo directory instead of or in addition to the Workframe, since this takes care of all the above.

Basic KrClass objects
All public objects and include files are preceeded by "Kr" to show that they belong to the KrClass library. Private symbols not intended to be of any importance to the user start with "_Kr". I hope that this way, they don't interfere with any other class libraries.

The next few pages will explain some of the basic tasks that can be done using Kroni's Classes.

The asynchronous main function
#include "krasyncm.hpp"

One of the biggest problems for beginners in PM programming is the fact that the programmer has to actively give OS/2 the chance to do even basic things like drawing the application's windows. That means that you can't just wait for user input in main and then continue doing some work in main. You have to actively leave main to draw the window which will let the user do some input.

The KrAsyncMain class works around this problem by letting you define your own main function, which will then run in a separate thread, while all the other tasks are still taken care of. Since the asynchronous main function runs in idle priority, it will always give way to important things like screen redrawing of your program (or of others), but it will also take all so-far unused CPU time.

Your main function will consist of a huge switch construction, of which only one branch leads to the "main code" itself. The other ones give you the chance to delay actions till the one you're just working at is done, and to react to user input.

To write your own asynchronous main function, do the following:
 * 1) Define a class which inherits from KrAsyncMain and forwards its constructor.
 * 2) Define the virtual function main of this class.
 * 3) Put a switch statement in there as shown in hello.cpp.
 * 4) Add more case statements to be able to post commands from within your own main function to make sure that other parts of it are executed later.
 * 5) Add case statements for window IDs of controls like Menues and PushButtons to be able to react to user command input like shown in ini.cpp.
 * 6) Create an instance of your new class directly before calling IApplication::current.run.

The common text output window
#include "krcto.hpp"

Especially when writing software that is not supposed to be published, it comes in extremely handy to use the common stream output mechanism to output text on the screen. Unfortunately, this won't work in PM programs since they don't have an appropriate text window by default.

The KrCommonTextOutput class uses a multi line edit control for this purpose. It inherits from IMultiLineEdit and can be treated exactly like other IMultiLineEdit objects. It features an ostream object which can be used for output in the very same way all other ostream objects (e.g. cout) can be used.

To include a common text output window in your program, do the following:
 * 1) Create the main window or the canvas you want to put the text output window into.
 * 2) Create an instance of KrCommonTextOutput using the IWindow object you created in step 1.
 * 3) The output stream can now be accessed using this instance's member function "stream".

To assign the stream to any ostream_withassign object (e.g. cout), do the following:

4.Use the operator "=" to assign the stream.

5.Set the flag "ios::unitbuf" on your target stream using the member function "setf" of the target stream.

Note:  This class is not suitable for large output or professional software, since it is rather slow and produces flickering during output. This is a restriction of the class IMultiLineEdit this class is based on.

The windowed communication class
#include "krwc.hpp"

To implement user input turns out to be even more difficult than user output in PM environments. The only acceptable way is to present the user a dialog window in which he may enter the input. The class KrWinComm constructs such dialogs from almost common stream input.

To prepare your program for user input via KrWinComm, do the following:

1.Create an instance of KrWinComm.

This needs to be done only once in your program.

To prepare a variable for input from the user, do the following:

2.Optionally, use the << operator with the previously created KrWinComm object and a string to define a text which informs the user about what he is supposed to type in.

3.Use the >> operator with the previously created KrWinComm object and the variable, just as you would do if you were using cin instead of the KrWinComm object.

To actualy display the dialog with all previously defined variables and change their values if user leaves the dialog by pressing OK, do the following:

4.Use either the >> operator or the << operator with the previously created KrWinComm object and the manipulator display.

Note:  Step 3 works only with objects derived from one of these classes:
 * IString
 * double
 * unsigned long
 * signed long
 * KrBitfield
 * KrChoice
 * KrUserDataField

With some work, it is also possible to input variables of other types.

The printer class

 * 1) include "krprint.hpp"

Though the UICL provides some support to draw graphics on the screen, it does nothing to help the user print his art. The KrPrinter class acts very similar to the UICL class IDrawingCanvas, except that it does not show the contents of its IGraphicList on the screen, but outputs it on a printer instead, thus solving this problem.

KrPrinter can use any printer installed in the OS/2 system. It also lets the user both choose the printer he wants to print on and change its settings.

To add printer support to your program, do the following:

1.Create an instance of the KrPrinter class.

Initially, all printer output will go to the system's default printer using its default settings. To present a dialog to the user so that he is able to change this, do the following:

2.Call the printDialog member function of the previously created KrPrinter object. You may call this function at any time and as often as you want to.

To actually print something, do the following:

3.Create an IGList object holding all the graphics you want to print.

4.Select the IGList object into the KrPrinter object by calling the member function setList of your KrPrinter object.

5.Call your KrPrinter object's member function print.

Drawing bitmap cached
#include "krbwin.hpp"

If you have ever put a large IGList into your IDrawingCanvas, you will most likely have hated the very long time the system needed for redrawing whenever you moved any window in front of your graphics output window. You can now get rid of this by drawing into a memory bitmap and then putting this memory bitmap instead of all the time-consuming graphics primitives into your IDrawingCanvas.

To create a memory bitmap, do the following:

1.Create an instance of KrMemoyBitmap.

To draw into the memory bitmap, do the following:

2.Create an IGList object holding all the graphics you want to display.

3.Select the IGList object into the KrMemoyBitmap object by calling the member function setList of your KrMemoyBitmap object.

To draw the memory bitmap's contents on the screen, do the following:

4.Create an IDrawingCanvas object.

5.Create an IGList object and add your KrMemoyBitmap object to it.

6.Select the IGList object into the IDrawingCanvas object.

Using your own coordinated systems
'''possibly #include "krgobj.hpp"

possibly #include "krbwin.hpp"


 * 1) include "krcstran.hpp"''' if none of the above was included

Drawing graphics in the system's coordinate system can sometimes be very complicated, because (a) the window size and hence the coordinate system size is usually unknown at compile time, (b) the need to use integer variables sometimes prevents exact placement, and (c) there's no way to get a logarithmic scale.

While (a) may partly be solved by using a transformation matrix and (c) is most likely interesting for scientists only, it would still be much more user friendly if the programmer could place all items within the coordinate system he chooses.

To define a coordinate system, do the following:

1.Create an instance of KrCoordSystemTranslator.

You can use this coordinate system to create instances of all classes which are defined in krgobj.hpp. You may also define your own classes. In addition, you may assign a memory bitmap to the coordinate system. If you do the latter, be certain to not display the bitmap in any common IDrawingCanvas, but only in an KrDrawingCanvas, and be sure to insert it into a KrGraphicList instead of an IGList.

You may collect your graphics in a KrGraphicList object and display them in a KrDrawingCanvas or print them using a KrPrinter. This is done using the common UICL methods. Refer to demo.cpp and to the IBM open class library documentation for details.

Using other than the predefined types for user input
There are generally three ways to define input for other than the predefined objects.

1.Create a subclass from KrUserDataField and wrap your input variable into an instance of that subclass. This is demonstrated in demo.cpp.

2.Write an overload for the >> operator for the type of your input variable. This is done the same way as for common text mode streams, except for the declaration of the operator itself, which must be:

KrWinComm & operator >> (KrWinComm & aWinComm, YourType & aObject);

3.Create a subclass from KrUserDataField and write a constructor for it which expects as its only argument a pointer to the structure you want to input. Then write an overload for the >> operator. This is demonstrated in dialog3.cpp.

Notes:

1.Method 1 and 3 are very flexible, because using this method you can create an arbitrary subwindow for the user input. Method 2 lets you only stack subwindows for already known input types.

2.Method 2 is very convenient, since there's no need to explicitly define a wrapper object when querying the user for input. Method 1 requires more work and Method 3 most work of all three methods.

3.Method 3 is most powerful, since it can be used to input arbitrary types (as opposed to method 1, which is restricted to classes inheriting from KrUserDataField), and the input can be done using arbitrary windows (as opposed to method 2). In most cases, it is a good idea to choose method 3.

Using your own graphic classes with a KrCoordSystemTranslater
To let your own graphic objects make use of you own coordinate system, have a look at krgobj.hpp and krgobj.cpp and study the definition of KrGLine. If you plan to make use of a logarithmic scale, please don't forget the additional work this will need.

Frames
Especially when printing, it is often convenient to use a rectangle within the whole window for output, while the graphic objects that print their output into that rectangle still think that the rectangle is the whole window.

This can be achieved using frames. Have a look at demo.cpp for how to use them.

Note:  Never forget to release each frame using a KrAntiFrame. If you fail to do so, repainting of your window will take place inside a frame instead of the whole window next time, and on the next redraws this will even become nastier.