Programmer Tools for Actor 3.0

The Windows 3.0 surge spurred Marty to take a look at Actor 3.0 and two of its support tools--WinTrieve and the Whitewater Resource Toolkit.


November 01, 1990
URL:http://www.drdobbs.com/tools/programmer-tools-for-actor-30/184408446

NOV90: PROGRAMMER TOOLS FOR ACTOR 3.0

Data management and resource editing for Windows development

This article contains the following executables: ROLO.ARC

Marty is a software engineer with Allen Testproducts Inc. of Kalamazoo, Michigan. He is the author of Object-Oriented Programming Featuring Actor, published by Scott Foresman & Co.


A colleague of mine (who shall remain nameless) has a favorite aphorism: If a program is tough to write, it should be tough to use. Given this mind-set, it should come as no surprise that he dislikes writing programs for Microsoft Windows, especially in C. I don't agree with this statement, but I do agree with its contrapositive: If a program is easy to use, it should be easy to write. Therefore, I have avoided using C to program for Windows. Instead, I use Actor.

The Whitewater Group, mindful of Actor's emergence as a serious Windows development language, has decided to provide two new programming tools for Actor: WinTrieve, an ISAM (Indexed Sequential Access Method) for Actor (and C) programs running under Windows; and the Whitewater Resource Toolkit, an editor for building and editing all the miscellaneous resources (menus, accelerators, cursors, and so on) needed by a Windows program.

Before these tools were around, developing comparable programs for Windows meant writing these programs in C. In this article, I'll examine these tools and how they're used to build an application that actually uses "real" files and Windows resources -- but with considerably less suffering.

Actor 3.0

Although Actor 3.0's support of Windows 3.0 was what made the news, there were other improvements over Version 2.0 as well. In Actor 2.0, for instance, an innovative "memory swapping" technique was implemented (see the article "Object Swapping" by Jan Bottorff and Jim Bolland, DDJ, May 1990) whereby the 640-Kbyte barrier was effectively broken, allowing you to write extremely large Actor programs. With Actor 3.0, this technique has been extended to use Windows' standard and enhanced 80386 modes to create applications up to 2 Mbyte in size.

Another feature worth mentioning is a streamlined seal-off procedure. The term "seal-off" refers to the process of removing the development classes from the workspace so just the application objects and classes remain: In effect, "compiling" the Actor program so it can run as a standalone Windows application. In prior versions of Actor, this could be cumbersome. In Versions 2.0 and 3.0, there's a menu option and a dialog to fill out, and the rest of the seal-off process is automatic.

There are also numerous other improvements in the product, including a System object for encapsulating operating system dependencies, and more Windows-specific classes. The new Windows classes include convenient Windows printing and dynamic menus and dialogs. The class library improvements have been added with an eye toward compatibility with prior versions of Actor, a lesson Borland and Microsoft could learn from the Whitewater Group.

Dynamic dialog and menu classes make rapid application prototyping much easier. In earlier versions of Actor, there were only simple modal and nonmodal dialogs available as classes without resorting to the Actor's Windows Call facility. This new version allows complex dialogs to be built with more object-oriented means, as shown in Example 1.

Example 1: Constructing and returning new dialog

  /* Build a record dialog.  */
  Def recDlg(self, dPhone, dPerson, dCompany | D)
  { D := new (DialogDesign);
    setSize(D, 8@8, 185@120);
    addItem(D, newStatic(DlgItem, "Phone:", 100, 5@10, 40@10, 0));
    addItem(D, newEdit(DlgItem, dPhone, PHONE, 50@10, 35@12, 0));
    addItem(D, newStatic(DlgItem, "Person:", 100, 5@25, 40@10, 0));
    addItem(D, newEdit(DlgItem, dPerson, PERSON, 50@25, 125@12, 0));
    addItem(D, newStatic(DlgItem, "Company:", 100, 5@40, 40@10, 0));
    addItem(D, newEdit(DlgItem, dCompany, dCompany, 50@40, 35@12, 0));
    addItem(D, newButton(DlgItem, "Cancel", IDCANCEL, 115@95, 40@14, 0));
    ^D;
  }

In the recDlg( ) method shown in Example 1, a new dialog is constructed and returned. The setSize( ) message gives the overall dimensions of the dialog box, while each addItem( ) adds a control or field. Static text, edited fields, and a pushbutton to cancel the dialog are added, along with their control IDs. Dynamic Dialogs are used in the sample program to create a multi-field input dialog for the record being added or updated in the database.

Other Actor 3.0 features include being able to more easily go between Actor and C or Assembler, including writing your own primitives in C and linking them into the Actor image.

WinTrieve

WinTrieve is a data manager for both Actor and C that provides an Indexed Sequential Access Method (ISAM) for Windows programs. Without it, fledgling Windows programmers must write their application using only the file access provided through the basic Actor or C language. This means there's no support for addition and retrieval of keyed records. Since many (if not most) business-type applications require this type of function, it meant hand-building this support into Actor or C programs -- not the most entertaining job.

WinTrieve provides the capability to create, add, delete, and retrieve records using a key. While not a complete database-management system, this support goes a long way to building a DBMS and in fact provides a sizable chunk of the function needed by most business programs. WinTrieve also provides some limited relational capabilities, such as linking fields between ISAM files. Locking is available in a fully automatic mode, a manual mode, or none. Both entire files and individual records may be locked.

WinTrieve has three parts: An ISAM server, which runs as a separate Windows application, and two Application Program Interfaces (APIs), one for Actor, the other for C. The Actor API consists of several classes that are loaded into the Actor image. These include an IsamManager class, an IsamFile class, and several IsamKey classes. The C API consists of header files and libraries (one for each memory model) and can be linked into your Microsoft C programs.

The ISAM Manager is loaded as a separate Windows program and runs "outside" of the Actor workspace. This means that programs written outside of Actor can use it, provided they adhere to the protocol. This has been documented in the WinTrieve manual and shouldn't prove any problem to experienced Windows programmers.

Using an object-oriented language has advantages when writing programs that use an ISAM. The OOP metaphor means you can think about the ISAM file as another collection or file object. Once written, the often-convoluted logic of dealing with recovery, transaction locking, and journaling can be safely placed in the class tree and inherited without a lot of fuss later.

WinTrieve is an access method, providing the application with services to build and use indexed files; it's not a complete DBMS. This limitation is both good news and bad. The downside is that you don't get a lot of the features of a full DBMS -- query facilities and application and report generators. The good news is that you get a simpler, more flexible tool with considerably less overhead. If you need the additional DBMS-like facilities, you can build them in Actor or C.

Whitewater Resource Toolkit

One of the limitations in earlier versions of Actor was that programmers were dependent on the Microsoft Windows SDK for customizing application resources. What a drag. The power of a state-of-the-art language such as Actor was harnessed in tandem with a plowhorse like the Resource Compiler. It made using static resources in Actor programs a chore, enough so -- unless you were writing production-quality programs in Actor -- that you didn't do it.

The Whitewater Resource Toolkit is a solution to this problem. It supports both C and Actor programs and can handle resources in RES, BMP, ICO, CUR, and EXE formats. The individual editors in it work like MacDraw or MacPaint: You are presented with a graphical view of the resource you are editing and you use a selection of tools, chosen from a palette, to edit them.

At the time I'm writing this, the Resource Toolkit doesn't run support for Windows 3.0. But, by the time you're reading this, the Resource Toolkit will likely support Windows 3.0. Until then, you'll have to edit resources under Windows 2.1, and bring them over.

Within the Resource Toolkit are editors for each type of resource: graphics editors (for bitmaps, cursors, and icons), a menu editor, a dialog editor, an accelerator editor, and a string editor. These editors are all linked by a tool called the "Mover," which allows you to view, move, copy, select, and delete various resources in various files.

The editors are all organized along the same lines. A File menu loads and saves the resource you're editing, and an Edit menu cuts, copies, and pastes parts of a shape. In the graphics editors, the palettes have tools for drawing lines, rectangles, and ellipses, and moving shapes and changing their colors.

The Resource Toolkit is easy and fun to use and makes you want to customize your application beyond the limits of good taste and user-friendliness. It's also entirely written in Actor and makes a convincing demonstration of what the language is capable of.

Putting it to Work

To demonstrate and evaluate these new tools, I took the Book Browser application provided with WinTrieve and modified it into a Rolodex file. ROLO.H (Listing One, page 132) is the application header file. As in a C program, this file contains constants, including menu item numbers and dialog constants.

ROLO.ICO is the Rolodex application icon. I took the icon provided for the Book Browser and modified it using the Resource Toolkit, adding a spiral binding to the side of the "book." Voila! instant Rolodex.

ROLO.ACT (Listing Two, page 132) adds some additional methods to the String class for the Rolodex. ROLO.RC is the resource file for the sealed-off application. To build this file, I simply modified what was in the Book Browser's resource file using Actor's File Editor.

ROLOAPP.CLS (Listing Three, page 132), ROLOFILE.CLS (Listing Four, page 132), and ROLOWIND.CLS (Listing Five, page 132) are all class files for the application. Listing Three implements the RoloApp class, the "wrapper" for the sealed-off application. It has a single message, init( ), sent when the application is started. It performs necessary initialization and passes control to RoloWindow, the application's main class. One of the initialization steps performed in init( ) is to start the ISAM Manager, and either create or open the Rolodex database.

ROLOFILE (Listing Four) is a descendant of the IsamFile class. It defines the structure of the ISAM file that contains the Rolodex. The ISAM file has three fields: The entry's phone number, the primary key, the person's name, a secondary key, and the person's company affiliation. All these fields are 30-character strings. The sole purpose of this class is to pass this initialization information to the ISAM Manager when the application is started.

Finally, ROLOWIND (Listing Five) implements the workhorse of the application; the RoloWindow class. Since we're developing a standalone Windows program, it'll also be the window that gets control when the program starts. The RoloWindow class is a descendant of TextWindow, so it automatically inherits all of that class's features, including text entry and a text cursor. But the RoloWindow class adds a menu handler that performs the functions of the application, including perusing the database by moving forwards and backwards and inserting and deleting entries.

The RoloWindow class is relatively large because it must support an elaborate protocol for Windows. This includes not only the messages needed to handle menu commands to the window, but also each menu function's message, such as deleteRec( ) to remove the current record from the file and updateRec( )to update the current record if it's been changed.

The bulk of the work in RoloWind. CLS is done in validateInput( ) and its private messages getPerson( ) and getPhone( ). These obtain and validate input from the user using Actor's Dynamic Dialogs. The recDlg( ) message constructs this dialog when the RoloWindow is initialized using several addItem( ) messages. It builds a Dictionary containing the name of each field as a key symbol and the field's contents as the value. These are not to be confused with the actual ISAM records, which have the same field names but are in all uppercase.

Products Mentioned

Actor 3.0 $695 Whitewater Resource Toolkit 1.0 $195 WinTrieve 1.0 $395

The Whitewater Group 1800 Ridge Ave. Evanston, IL 60201-3621 800-869-1144

The amount of code in RoloWindow is reduced by using Actor's powerful perform( ) message. This allows us to create a collection of methods and messages for all the possible responses to RoloWindow's command( ) message. When the command( ) message is sent with a menu number after a menu item is picked by the user, command( ) simply passes control to the method matching the menu ID. No multiway branches are needed. This means that new menu options can be easily added to the Rolodex later.

To get the application working, you must first load the ISAM Manager's classes into the Actor workspace. On my home PC (a 10-MHz 286 with only 640 Kbyte of RAM) I had to chop Actor's dynamic and static storage allocations before both the ISAM Manager and Actor could coexist under Windows. The Whitewater Group recommends having at least 1 Mbyte of RAM when using WinTrieve and Actor together. On my work PC, a 20-MHz 386 with 2 Mbyte of RAM, this was not a problem. The sealed-off application, however, ran fine on both machines.

Once the ISAM manager is loaded, you need to load the Rolodex classes by loading the ROLO.LOD load file. You can then start the Rolodex interactively using:

  Sam := new(RoloApp);
  init(Sam, nil);

Conclusion

Actor was always a convenient programming tool for Windows, and a good way to become familiar with Windows' facilities even if you wrote the final program in C. With Version 3.0, Actor is now better suited to developing real applications. The addition of WinTrieve and the Whitewater Resource Toolkit boost Actor's capabilities as a professional applications and rapid prototyping language. Programmers despairing of C for writing Windows programs are encouraged to consider Actor for their next programming project. You might find that programs that are easy to use can also be easy to write.

_PROGRAMMER TOOLS FOR ACTOR 3.0_ by Marty Franz

[LISTING ONE]



/* ROLO.H */

/* Menu Constants */
#define SEARCH          2000

#define PRIMARY         2011  /* Index popup */
#define ALTERNATE       2012

#define NEXT            2020
#define PREV            2030
#define INSERT          2040
#define UPDATE          2050
#define DELETE          2060
!!

/* Dialog Constants */
#define PHONE           101
#define PERSON          102
#define COMPANY         103
!!





[LISTING TWO]


/* Additional methods required by WinTrieve Rolodex Browser sample app. */

now(String);!!

/* Returns a copy of the receiver from
  which a blank characters have been removed. */
Def removeBlanks(self | str)
{ str := "";
  do(self,
  {using(c) if c <> ' ' then str := str + asString(c) endif;
  });
  ^str;
}
!!
now(class(CType))!!

/* Return a CType object by looking up it's name in type dictionaries. Same as
   findType method except does no error checking. */
Def getType(self, tName)
{ ^$CTypes[tName] cor $UserTypes[tName];
}
!!




[LISTING THREE]


/* Rolodex browser. */!!

inherit(Application, #RoloApp, nil, 2, nil)!!

now(class(RoloApp))!!

/* Remove unnecessary classes. */
Def removeExtra(self)
{ do(#(EditWindow WinEllipse WinPolygon StopWatch
       Control FileDialog ReplaceDialog RelFile),
  {using(g) removeGlobal(self, g);
  });
}
!!

now(RoloApp)!!

/* Startup the Rolodex browser. */
Def init(self, cmdStr)
{ init(self:ancestor, cmdStr);
  mainWindow := newMain(RoloWindow, nil, "Rolodex Browser", nil);
  show(mainWindow, CmdShow);
  if not(createDB(mainWindow))
  then close(mainWindow);
  endif;
}
!!

/* Class Initialization */




[LISTING FOUR]


/* A Rolodex file. */!!

inherit(IsamFile, #RoloFile, nil, 2, nil)!!

now(RoloFileClass)!!

now(RoloFile)!!

/* Init Rolodex file record type and key defs. */
Def init(self)
{ init(self:ancestor);
  def(UserType, #rolo, #(
    char    phone       30
    char    person      30
    char    company     30
  ));
  setRecType(self, #rolo);
  addKeyDef(self, #primary, #NODUPS, #phone);
  addKeyDef(self, #person, #DUPS, #person);
}!!




[LISTING FIVE]


/* Main window of Rolodex ISAM file browser. */!!

inherit(TextWindow, #RoloWindow, #(keysDict  /* Dictionary of keys */
roloDB    /* ISAM manager */
roloTable /* ISAM rolodex file */), 2, nil)!!

now(class(RoloWindow))!!

now(RoloWindow)!!

/* Initiate a session with the ISAM manager. Create the ISAM file. */
Def createDB(self)
{ roloDB := new(IsamManager);
  openManager(roloDB);
  if checkError(roloDB)
  then destroy(roloDB);
    ^roloDB := nil;
  endif;
  roloTable := new(RoloFile);
  setFilename(roloTable, "rolo");
  setManager(roloTable, roloDB);
  create(roloTable, ISINOUT + ISMANULOCK);
  if checkError(roloTable)
  then destroy(roloTable);
    ^roloTable := nil;
  endif;
}
!!
/*  Handles input dialog processing for obtaining search title. Returns title
   or nil if user cancels. */
Def getPerson(self | id title)
{ id := new(InputDialog, "Person Key", "Person:", "");
  loop
  while runModal(id, INPUT_BOX, self) = IDOK
    title := leftJustify(getText(id));
    if size(title) > 0
    then ^title;
    endif;
  endLoop;
  ^nil;
}
!!
/* Handles input dialog processing for obtaining phone number. Returns phone
   number or nil if user cancels. */
Def getPhone(self | id str val)
{ id := new(InputDialog, "Primary Key", "Phone:", "");
  loop
  while runModal(id, INPUT_BOX, self) = IDOK
    str := removeBlanks(getText(id));
    if size(str) > 0 cand (val := asInt(str, 10))
    then ^val;
    endif;
  endLoop;
  ^nil;
}
!!
/* Validate record dialog input. If ok, returns dictionary of input field
  values. If error input field, displays error box and returns nil. */
Def validateInput(self, cVals | input)
{ input := new(Dictionary, 10); /* Validate input. */
  input[#phone] := leftJustify(removeBlanks(cVals[PHONE]));
  if size(input[#phone]) = 0
  then errorBox(caption, "Invalid Phone field.");
  else input[#person] := leftJustify(cVals[PERSON]);
    if size(input[#person]) = 0
    then errorBox(caption, "Invalid Person field.");
    else input[#company] := leftJustify(cVals[COMPANY]);
      if size(input[#company]) = 0
      then errorBox(caption, "Invalid Company field.");
      else ^input
      endif;
    endif;
  endif;
  ^nil;
} !!
/* Close the database. */
Def closeDB(self)
{ if roloTable
  then close(roloTable);
    destroy(roloTable);
    roloTable := nil;
  endif;
  if roloDB
  then closeManager(roloDB);
    destroy(roloDB);
    roloDB := nil;
  endif;
}
!!
/* Closing the window so close the database. */
Def shouldClose(self)
{ closeDB(self);
}
!!
/* Initiate a session with the ISAM manager. */
Def openDB(self)
{ roloDB := new(IsamManager);
  openManager(roloDB);
  if checkError(roloDB)
  then destroy(roloDB);
    ^roloDB := nil;
  endif;
  roloTable := new(RoloFile);
  setFilename(roloTable, "rolo");
  setManager(roloTable, roloDB);
  open(roloTable, ISINOUT + ISMANULOCK);
  if checkError(roloTable)
  then destroy(roloTable);
    ^roloTable := nil;
  endif;
}
!!
/* Display the current record in the window. */
Def printRecord(self)
{ cls(self);
  printString(self, "Phone:     ");
  printString(self, asString(getField(roloTable, #phone)));
  eol(self);
  printString(self, "Person:    ");
  printString(self, asString(getField(roloTable, #person)));
  eol(self);
  printString(self, "Company:    ");
  printString(self, asString(getField(roloTable, #company)));
  eol(self);
}
!!
/* Build a record dialog. */
Def recDlg(self, dPhone, dPerson, dCompany | D)
{ D := new(DialogDesign);
  setSize(D, 8@8, 185@120);
  /*
  addItem(D, newStatic(DlgItem, "Phone:", 100, 5@10, 40@10, 0));
  addItem(D, newEdit(DlgItem, dPhone, PHONE, 50@10, 35@12, 0));
  addItem(D, newStatic(DlgItem, "Person:", 100, 5@25, 40@10, 0));
  addItem(D, newEdit(DlgItem, dPerson, PERSON, 50@25, 125@12, 0));

  addItem(D, newStatic(DlgItem, "Company:", 100, 5@40, 40@10, 0));
  addItem(D, newEdit(DlgItem, dCompany, COMPANY, 50@40, 35@12, 0));
  */
  addItem(D, newStatic(DlgItem, "Phone:", 100, 5@10, 40@10, 0));
  addItem(D, newEdit(DlgItem, dPhone, PHONE, 50@10, 125@12, 0));
  addItem(D, newStatic(DlgItem, "Person:", 100, 5@25, 40@10, 0));
  addItem(D, newEdit(DlgItem, dPerson, PERSON, 50@25, 125@12, 0));
  addItem(D, newStatic(DlgItem, "Company:", 100, 5@40, 40@10, 0));
  addItem(D, newEdit(DlgItem, dCompany, COMPANY, 50@40, 125@12, 0));
  addItem(D, newButton(DlgItem, "Cancel", IDCANCEL, 115@95, 40@14, 0));
  ^D;
}
!!
/* Search for a record based on current index order. */
Def searchRec(self, wp | val)
{ select
    case currentIndex(roloTable) = #primary
    is val := getPhone(self);
      if not(val)
      then ^nil;
      endif;
      putField(roloTable, val, #phone);
    endCase
    case currentIndex(roloTable) = #person
    is val := getPerson(self);
      if not(val)
      then ^nil;
      endif;
      putField(roloTable, val, #person);
    endCase
  endSelect;
  if not(read(roloTable, ISEQUAL))
  then checkError(roloTable);
    ^nil;
  endif;
  printRecord(self);
}
!!
/* Read the previous record and display it. */
Def prevRec(self, wp)
{ if read(roloTable, ISPREV)
  then printRecord(self);
  else checkError(roloTable);
    cls(self);
  endif;
}
!!
/* Read the next record and display it. */
Def nextRec(self, wp)
{ if read(roloTable, ISNEXT)
  then printRecord(self);
  else checkError(roloTable);
    cls(self);
  endif;
}
!!
/* Insert a record. */
Def insertRec(self, wp | rDlg cVals iVals)
{ rDlg := recDlg(self, "", "", "");
  loop
  while runModal(rDlg, nil, self) <> 0
    cVals := controlValues(rDlg);
    if iVals := validateInput(self, cVals)
    then putField(roloTable, iVals[#phone], #phone);
      putField(roloTable, iVals[#person], #person);
      putField(roloTable, iVals[#company], #company);
      if not(insertCurrent(roloTable))
      then checkError(roloTable);
      else printRecord(self);
      endif;
      ^self;
    endif;
    rDlg := recDlg(self, cVals[PHONE], cVals[PERSON], cVals[COMPANY]);
  endLoop;
}!!
/* Change to selected index and display first record in new index ordering. */
Def changeIndex(self, wp | key oldKey)
{ key := keyAt(keysDict, wp);
  oldKey := currentIndex(roloTable);
  if key = oldKey
  then ^self;
  endif;
  if selectRecord(roloTable, key, 0, ISFIRST)
  then read(roloTable, ISNEXT);
    unCheckMenuItem(menu, keysDict[oldKey]);
    checkMenuItem(menu, wp);
  endif;
  if not(checkError(roloTable))
  then printRecord(self);
  endif;
}
!!
/* Delete the current record. */
Def deleteRec(self, wp)
{ if not(deleteCurrent(roloTable))
  then checkError(roloTable);
  else cls(self);
  endif;
}
!!
/* Update a record. */
Def updateRec(self, wp | rDlg cVals iVals)
{ if not(read(roloTable, ISCURR))
  then checkError(roloTable);
    ^self;
  endif;
  rDlg := recDlg(self,
    asString(getField(roloTable, #phone)),
    getField(roloTable, #person),
    asString(getField(roloTable, #company)));
  loop
  while runModal(rDlg, nil, self) <> 0
    cVals := controlValues(rDlg);
    if iVals := validateInput(self, cVals)
    then putField(roloTable, iVals[#phone], #phone);
      putField(roloTable, iVals[#person], #person);
      putField(roloTable, iVals[#company], #company);
      if not(updateCurrent(roloTable))
      then checkError(roloTable);
      else printRecord(self);
      endif;
      ^self;
    endif;
    rDlg := recDlg(self, cVals[PHONE], cVals[PERSON], cVals[COMPANY]);
  endLoop;
}!!
/* Respond to the menu events.
  The wp argument gives the selected menu ID.
  Get a message symbol from the menu object. */
Def command(self, wp, lp | msg)
{ if msg := action(menu, wp)
  then ^perform(self, wp, msg)
  endif;
}!!
/* Setup the menu bar. */
Def createMenu(self)
{ createMenu(self:ancestor);
  menu := init(new(Menu));
  setHandle(menu, hMenu);
  topMenu(menu, "&Search!", SEARCH, #searchRec);
  keysDict := new(OrderedDictionary, 4);
  keysDict[#primary] := PRIMARY;
  keysDict[#person] := ALTERNATE;
  popupMenu(menu, "&Index",
    tuple("phone", "person"),
    tuple(PRIMARY, ALTERNATE), #changeIndex);
  checkMenuItem(menu, PRIMARY);
  topMenu(menu, "&Next!", NEXT, #nextRec);
  topMenu(menu, "&Prev!", PREV, #prevRec);
  topMenu(menu, "&Insert!", INSERT, #insertRec);
  topMenu(menu, "&Update!", UPDATE, #updateRec);
  topMenu(menu, "&Delete!", DELETE, #deleteRec);
  drawMenu(self);
}
!!
/* Initialize the window. Create menu and About to control menu. */
Def init(self)
{ init(self:ancestor);
  createMenu(self);
  addAbout(self);
}
!!

/* Class Initialization */


[ROLO.LOD]

/* Load file for WinTrieve Rolodex Browser.  You must load
   ISAM.LOD file before loading these files. */

LoadFiles := tuple(
  "classes\menu.cls",           /* dynamic menu support */

  "res\control.h",              /* dynamic dialog support */
  "classes\dlgitem.cls",
  "classes\dialogde.cls",

  "res\rolo.h",                 /* rolo Browser support */
  "classes\rolofile.cls",
  "classes\rolowind.cls",
  "classes\roloapp.cls",
  "act\rolo.act"
)!!

printLine("");!!
printLine("Use load() to load WinTrieve Rolodex Browser");!!


[EXAMPLE 1]

/* Build a record dialog. */
Def recDlg(self, dPhone, dPerson, dCompany | D)
{ D := new(DialogDesign);
  setSize(D, 8@8, 185@120);
  addItem(D, newStatic(DlgItem, "Phone:", 100, 5@10, 40@10, 0));
  addItem(D, newEdit(DlgItem, dPhone, PHONE, 50@10, 35@12, 0));
  addItem(D, newStatic(DlgItem, "Person:", 100, 5@25, 40@10, 0));
  addItem(D, newEdit(DlgItem, dPerson, PERSON, 50@25, 125@12, 0));
  addItem(D, newStatic(DlgItem, "Company:", 100, 5@40, 40@10, 0));
  addItem(D, newEdit(DlgItem, dCompany, dCompany, 50@40, 35@12, 0));
  addItem(D, newButton(DlgItem, "Cancel", IDCANCEL, 115@95, 40@14, 0));
  ^D;
}











Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.