Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Inside Object Professional


SEP90: INSIDE OBJECT PROFESSIONAL

INSIDE OBJECT PROFESSIONAL

A rich set of objects is the key to object-oriented development in Turbo Pascal

Gary Entsminger

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.

Pick an Object

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.

Figure 1: Subset of the Object Professional object hierarchy.

  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.

Figure 2: Selected children of the Root object type

  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.

Data Entry

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.

Figure 3: The field hierarchy derived from DoubleListNode and the abstract field.

  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.

TSRs

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.

Command Processing

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.

Figure 5: Example command codes from the OPCMD unit

  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.

Command Windows

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.

The Downside?

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?

Products Mentioned

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]

<a name="01e1_0010">

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.



<a name="01e1_0011"><a name="01e1_0011">
<a name="01e1_0012">
[LISTING TWO]
<a name="01e1_0012">

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.




<a name="01e1_0013"><a name="01e1_0013">
<a name="01e1_0014">
[LISTING THREE]
<a name="01e1_0014">

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.










Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.