Gary takes a look at Turbo Power's Object Professional, an object-oriented library for Turbo Pascal.
September 01, 1990
URL:http://www.drdobbs.com/cpp/inside-object-professional/184408413
Gary is a writer, programmer, and a consultant. He is the co-author (with Bruce Eckel) of Tao of Objects to be published later this year. Gary can be reached at Rocky Mountain Biological Lab., Crested Butte, CO 81224, or through CompuServe: 71141,3006.
Object Professional, from Turbo Power Software, is a splendid library of object-oriented classes for Turbo Pascal 5.5 programmers. It's extensive -- consisting of over 50 units, 130 object types, 2100 documented procedures, functions, and methods, 1200 additional internal routines, and 1600 pages of text and examples neatly divided into 3 manuals.
Object Professional includes complete Turbo Pascal and assembly language source code so you can modify, append, and derive to your heart's delight. If you're a Turbo Pascal 5.5 programmer developing applications that need sophisticated screen management, data input/output, or TSR routines, check out this package. It could save you months of development time.
Object Professional includes objects for screen, keyboard, and mouse handling; windows, menus, pick lists, and directories; text editing and viewing; data entry and code generation for data entry screens; printing typefaces, control sequences, and forms; data objects ("container classes"), lists, sets, and so on; handling longer than 255 character strings; interfacing to DOS; creating TSRs that automatically swap to EMS or disk; memory allocation and deallocation; keyboard macros; BCD arithmetic; and more. The list of objects really goes on and on.
Much of Object Professional is a translation of Turbo Professional, Turbo Power's state-of-the-art library of non-object-oriented subroutines for Turbo Pascal 4.0 and 5.0, and much of Object Professional is new. Container classes, a swapping EXEC function, improved windows and mouse support, large arrays that can use RAM, disk, or EMS, text and binary file viewing, complete window-oriented text editing, and several utilities for making data entry screens and menus have been added. It would (literally) take a dozen pages just to list the new procedures and functions. Instead of doing that, let's take another approach toward examining Object Professional. One that focuses on the object-oriented structure of the library.
Figure 1 shows a portion of Object Professional's object hierarchy. The hierarchy is well-thought out and is thorough. Abstract objects (such as AbstractField, AbstractFrame, AbstractWindow, AbstractSelector, AbstractHelpWindow, and AbstractArray) are built into the library making it easy to derive new objects from Toolbox objects and to implement polymorphism.
ColorSet Root PointerStack WindowStack ByteStack StaticQueue SingleListNode TextField PrintMode ... DoubleListNode HeaderNode ShadowNode HotNode MenultemNode AbstractField SelectField EntryField StringField ArrayField PrintField StringPrintField CharPrintField ... LineField ShadedField SingleList DoubleList CircularList MenultemList BitSet LargeBitSet StringDict StringSet StringArray AbstractArray RAMArray EMSArray VirtualArray ... OPArray DirEntry IDStream DOSIDStream BufIDStream Library MemIDStream MemLibrary CommandProcessor CommandPacker Cloner ScrollBar ScreenRect LoadableColorSet PackedWindow VitScreen LineEditor SimpleLineEditor AbstractFrame Frame AbstractWindow RawWindow SubMenu StackWindow CommandWindow Browser Memo MemoFile TextEditor Menu PickList DirList Pathlist AbstractHelpWindow PagedHelpWindow ScrollingHelpWindow AbstractSelector Selector ScrollingSelector EntryScreen ScrollingEntryScreen BasePrinter DOSPrinter FlexiblePrinter BiosPrinter Printer Report
Root is the ancestor of all the objects in the library except ColorSet. The highest level objects (and the most complex) are the most indented in the hierarchy. Note well that Object Professional is primarily built up from data structures. Various lists (single, double, and so on), arrays, and windows, for example, are ancestors for many of the objects in the toolbox (see Figure 2). A TextEditor, for example, is a complex object that rivals the Turbo Pascal editor in power and flexibility. It's very complete -- with word wrapping, cursor positioning, extensive file I/O, block reads, writes, copies, moves, and its own editing and command windows. A text editor can be incorporated into an application very easily. Listing One (page 108) shows how little code is needed to create an editor window, read a file, edit the file, and save it.
Root AbstractWindow StackWindow CommandWindow Memo MemoFile TextEditor
In addition, the TextEditor offers two sets of programming hooks that allow you to completely customizes TextEditor objects for specific applications. One set takes procedure pointers as parameters, thus allowing you to specify which procedures will be called when a string, file name, or yes/no answer needs to be entered by a user.
Another set lets you implement your own commands -- to invoke an editor/window and move the cursor to a specific location in a text buffer, for example. You could first run a program (such as a compiler) and then use the results from that program (and this set of commands) to go to the position in the file that contains an error, to automatically search for and replace text, and so on.
In addition, the text editor object, like many of the objects in Object Professional can be made to automatically detect whether a mouse driver is currently loaded, and if so, use a complete set of mouse commands for moving, scrolling, controlling events, and so on. You can also create mouse command windows (more on command windows later) and incorporate them in TSRs. This can be accomplished entirely by using objects contained in the toolbox.
Object Professional's data entry objects are extensive, making it easy to design systems for editing single variables (such as lines or fields) and groups of variables (such as screens). Object Professional's line editor can be used for prompting users for file names, strings, and numbers. You can build entry screens for editing database records, spreadsheets, configuration files, command codes, and so on.
The fundamental structure of Object Professional's data entry system is the "abstract field," which is derived from a DoubleListNode. Any field derived from an abstract field possesses the facilities for belonging to and manipulating a list. The field hierarchy derived from first the DoubleListNode and then the abstract field looks like that shown in Figure 3.
DoubleListNode AbstractField SelectField EntryField StringField IntField ... MultiLineField PickField PrintField StringPrintField ... LineField ShadedField
An abstract field isn't associated with any data. Instead it contains the methods for formatting the variables which will be displayed on the screen or will be printed. An abstract field is the link between the data entry and printing facilities in the toolbox.
Using Object Professional's data entry objects, a programmer can derive a high-level object, such as an EntryScreen, which treats all fields alike, without knowing exactly which type a particular field contains. An EntryScreen could then display the contents of a field, for example, by calling the particular field's draw method, which decides (or knows) how to display the field. It can display the field itself, call a driver to display it, or whatever. This is a neat implementation of polymorphism at a high and practical level.
The data entry objects and units are probably the most complex and comprehensive in the toolbox. The manual devotes 250 pages to these units and the source code disks include several useful demos and examples which you'll want to use in order to better understand the Object Professional approach to object-oriented programming.
Figure 4 shows a screen captured from one of the data entry demos, MakeMenu. You can use MakeMenu to design a menu system interactively. When you're satisfied with the menu system you've designed, MakeMenu generates the source code for the menu system for you. MakeScreen, another demo included with Object Professional, lets you design data entry screens interactively, and then generates that source code for you. Nifty.
One of the most sophisticated units in Object Professional, the OPINT unit, isn't really object oriented -- it manages Interrupt Service Routines (ISRs). You can use this unit to create TSR (Terminate and Stay Resident) applications, which can automatically determine the presence of EMS (expanded memory) and/or a RAM disk or hard drive, and swap themselves out of executable memory (the first 640K) accordingly.
OPINT is basically an ISR manager. In short, it keeps track of interrupt handlers via interrupt handles. The InitVector function lets you specify a handle for each TSR (you can have up to 20), and then uses that handle to keep track of things. If you need to restore an interrupt vector, you can call Restore-Vector with the handle, and OPINT will furnish DOS with the correct interrupt number and the address of the old service routine. OPINT lets you build nearly bulletproof TSRs while programming at a very high level.
The ISR unit also contains routines that help you write large, complex TSRs. These routines allocate and deallocate alternate stacks, allow you to switch to alternate stacks, and call another procedure from inside an ISR, and let you chain to previous interrupt handlers. Best yet, Object Professional includes several demos (with source code) that are not only useful, but can be used to learn the nitty-gritty details of building complex TSRs with multiple, configurable hot keys.
Listing Two (page 108) shows enough code to create a TSR, which automatically swaps itself to EMS or to disk. The kernel that remains in executable memory is only 6K and will seldom need to be more. Because Object Professional's ISR routines swap all but the management kernel out of executable memory, larger programs don't need to use any more executable memory than smaller ones after a swap. The key to keeping the resident part (the kernel) of a swapping TSR small is to keep the main program small because the main program segment's code is always kept in memory.
One of the most useful units in Object Professional is OPCMD, which includes an object called a CommandProcessor. This object translates key sequences into command codes -- numbers that can represent all the commands in a program. You can then group these command codes into a set of tables that show the relationships between key sequences and commands (or actions). Any object (method, function, or procedure) can then call the CommandProcessor which gets a keystroke, scans the appropriate table, and returns the correct command (or action). An object using the CommandProcessor doesn't have to know anything about the actual key pressed. It simply acts on the command that's linked to the keystroke.
To get you started, the OPCMD unit defines a set of standard command codes as constants. For example, consider the command codes shown in Figure 5. To put you in control, OPCMD sets aside 55 "user exit commands," ccUser0 through ccUser54. You can assign these commands to a keystroke or a set of keystrokes. This makes it easy to add hotkey links to commands in many of the Object Professional units. For example, suppose you wanted to link the hotkey Ctrl F1 to a hypertext help system. You'd assign it a user exit command, say, ccUser1. Then any object using the command processor would automatically recognize the hotkey and return control of the system to the command assigned to the key. By using more than one table, a key can be linked to many different commands, depending on the situation.
ccQuit = 004;{quit processing} ccMouseSel = 006;{mouse action} ccUp = 012;{cursor up} ccNewFile = 112;{read a file} ccSaveFile = 113;{save a file} ccSubNext = 121;{go to next submenu} ccSubQuit = 122;{quit current menu}
Command processing of this type "protects" you from the low-level details of dealing directly with the keyboard. This saves a little programming time, but more important: Lets you quickly reassign keystrokes to commands, and makes it easy to reconfigure key assignments.
Listing Three (page 108) shows a short program that creates and processes a command window by associating keystrokes with the command codes defined in KeySet. Note that a KeySet is an array that can hold a virtually unlimited number of key definitions. The array size is determined by the size of the constant KeyMax.
Object Professional implements a complete set of windowing objects, which can be used to derive complex and colorful window objects. These include LoadableColorSet, PackedWindow, WindowStack, AbstractWindow, RawWindow, StackWindow, and CommandWindow.
The CommandWindow object links keyboard and mouse input to the various ways of displaying, viewing, and editing text. A CommandWindow essentially takes control of the keyboard (via the CommandProcessor), and makes it easy for each window to have its own commands and actions. Text editors, browsers, pick lists, directories, status lines, and so on can be processed within CommandWindows. When a CommandWindow takes control (via a CommandProcess) it doesn't need to know anything about the rest of the program. It's an object, with its own shape, state, and set of commands. Using CommandWindows (and a few other Object Professional objects and your imagination) you can create a desktop environment that's comparable to professional environments such as Turbo C++.
CommandWindow also includes hooks, which makes it easy to link an error handler and a help system to a CommandWindow. The error handling hook is a pointer to a procedure that's automatically called whenever there's an error. The help hook is a procedure for accessing topic-indexed (for example, hyper) help.
Object Professional CommandWindows are immediately useful, instructive, and trend setting. Useful because they're powerful and require a minimum of effort to get them up and running. Instructive and trend setting because they're a terrific lesson in thinking about programming OBJECTively and using windows as objects.
The Object Professional approach to command windows is in some ways similar to the approach one must take in order to program in graphic environments such as Microsoft Windows. Command (or message) processing is the name of the game in sophisticated graphic environments. Although the command windows you create with Object Professional can't be readily used in graphic environments, their conceptualization can be.
Really, for text processing, Object Professional doesn't have a "true" downside. The procedures, functions, and methods included in this toolbox are tight and fast; many are optimized in assembly language. The source code is complete, and well documented in the listings and in the manuals. The manuals (all 1600 pages of them) are top-notch. The organization, with chapters corresponding to units, is excellent. Each method includes sections on the declaration, purpose, description, example, and "see also." There is usually one, or at most, two methods to a page. The index could (of course) be a lot better! Nobody (including the big guys!) spends enough time on indices.
Because there isn't much I don't like about Object Professional, I'll conclude with a couple of (simple!) additional objects I'd like to see. In short, two more Object Professionals -- one for Turbo C++ and one for Microsoft Windows. A toolkit as powerful as Object Professional applied to an important graphic environment (such as Windows) would be a coup. Are you listening Turbo Power?
Object Professional Turbo Power Software P.O. Box 66747 Scotts Valley, CA 95066 408-438-8608 Price: $150 Requirements: IBM PC/compatible 640K RAM Hard disk, Turbo Pascal 5.5 or later
_INSIDE OBJECT PROFESSIONAL_ by Gary Entsminger
[LISTING ONE]
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.
program Edit;
uses OpCrt, OpRoot, OpCmd, OpFrame, OpWindow, OpMemo, OpEditor;
var
TE : TextEditor;
FSize : LongInt;
ExitCommand : Word;
AllDone : Boolean;
begin
if not TE.InitCustom(2, 4, 79, 24, { Window coordinates }
DefaultColorSet, { ColorSet }
DefWindowOptions or wBordered, { Win options }
65521) { Buffer size }
then
begin
WriteLn('Failed to init TextEditor. Status = ', InitStatus);
Halt;
end;
{ use built-in status and error handlers provided by OPMEMO }
TE.SetStatusProc(MemoStatus);
TE.SetErrorProc(MemoError);
{ Create and Read a text file }
TE.ReadFile('AnyFile', FSize);
AllDone := False;
repeat
TE.Process;
ExitCommand := TE.GetLastCommand;
case ExitCommand of
ccSaveExit, { Save and exit -- file already saved }
ccAbandonFile, { Abandon file }
ccError : { Fatal error }
AllDone := True;
{...user exit commands..}
end;
until AllDone;
TE.Erase;
TE.Done;
ClrScr;
end.
[LISTING TWO]
program HelloWorld;
Uses HWMain, OpSwap;
begin
HelloWorldMain;
end;
unit HWMain;
interface
uses
OpInLine, OpSwap1;
procedure HelloWorldMain;
implementation
const
HotKey = $080F { code for ALT-TAB }
Swap1 = 'Hello1.SWP';
Swap2 = 'Hello2.SWP';
{$F+}
procedure PopUpEntryPoint;
begin
Writeln('Hello World');
end;
{$F-}
procedure HelloWorldMain;
begin
SetSwapMsgOn( not WillSwapUseEMS(ParagraphsToKeep) );
{ define the popup }
if DefinePop(HotKey, PopUpEntryPoint, Ptr($SSeg,SPtr)) then
begin
Writeln('PopUp loaded, press <ALT><TAB> to activate.');
{ Make PopUp routines active. }
PopUpsOn;
{ Try to go resident. }
StayResSwap(ParagraphsToKeep, 0, Swap1, Swap2, True);
end;
{ If we get here, report failure. }
Writeln('Unable to go resident. ');
end;
end.
[LISTING THREE]
program CommandWindowExample; {EXCMDWIN.PAS}
uses
OpCrt, OpRoot, OpCmd, OpFrame, OpWindow;
const
{Define a trivial KeySet of a few cursor commands}
KeyMax = 18;
KeySet : array[0..KeyMax] of Byte = (
{length keys command type key sequence}
3, $00, $48, ccUp, {Up}
3, $00, $50, ccDown, {Down}
3, $00, $4B, ccLeft, {Left}
3, $00, $4D, ccRight, {Right}
2, $1B, ccQuit); {Esc}
type
SampleWindow =
object(CommandWindow)
procedure Process; virtual;
end;
var
Commands : CommandProcessor;
CmdWin : SampleWindow;
Finished : Boolean;
procedure SampleWindow.Process;
begin
repeat
{Get a command}
GetNextCommand;
case GetLastCommand of
ccUp : WriteLn('ccUp');
ccDown : WriteLn('ccDown');
ccLeft : WriteLn('ccLeft');
ccRight : WriteLn('ccRight');
ccQuit : WriteLn('ccQuit');
ccChar : WriteLn('ccChar: ', Char(Lo(GetLastKey)));
else WriteLn('ccNone');
end;
until (GetLastCommand = ccQuit) or (GetLastCommand = ccError);
end;
begin
{Make a small CommandProcessor}
Commands.Init(@KeySet, KeyMax);
{Make a bordered CommandWindow}
if not CmdWin.InitCustom(30, 5, 50, 15, {Window coordinates}
DefaultColorSet, {Color set}
wBordered+wClear+wSaveContents, {Window options}
Commands, {Command processor}
ucNone) {Unit code}
then begin
WriteLn('Failed to init CommandWindow. Status = ', InitStatus);
Halt;
end;
{Add headers and draw window}
CmdWin.wFrame.AddHeader(' Command window ', heTC);
CmdWin.wFrame.AddHeader(' <Esc> to Quit ', heBC);
CmdWin.Draw;
{Get and process commands}
Finished := False;
repeat
CmdWin.Process;
case CmdWin.GetLastCommand of
ccQuit : Finished := True; {Quit}
ccError : begin {Error}
WriteLn('Error: ', CmdWin.GetLastError);
Finished := True;
end;
ccUser0..ccUser55 : WriteLn('user command'); {Handle exit command}
end;
until Finished;
{Clean up}
CmdWin.Done;
Commands.Done;
end.