Toolkit Basics "No, what I said was 'No new taxis'" Georgio Bush, 1990 An Object Primer Let's talk cars. We all know that you don't need to be a combustion mechanic to drive a car (that's a veeehickle if you're from Texas), but most good drivers know a few basic principles about how a car works. The same philosophy is true for object oriented programming: you don't need to be an OOP guru to use the Toolkit but a little understanding is helpful. The modest goal of this section is to explain some of the object oriented principles to help you get up and running with the Toolkit as soon as possible. Over time, you will gain OOP expertise without even trying! If you are already familiar with OOP, skip to the next section. For now, we will concentrate on those aspects of OOP you will need in order to understand how to use the Toolkit. In Part 2: Extending the Toolkit, the more advanced concepts of inheritance and extensibility are addressed. In plain Pascal, there are types like integer, real, string, etc. and you declare variables to be of a specific type. You can even define new types. For example, the following declaration defines a new type Address: TYPE Address = record Name: string[20]; Street: string[30]; CityStZip: string[20]; end; Having defined the type Address, you can then create variables of this type, e.g. VAR BobsHouse: Address; In most programs, you will write procedures and functions that can manipulate the data stored in these variables, e.g. procedure PrintLabel(Addr: address); function Zip(Addr:address): string; 3-2 User's Guide -------------------------------------------------------------------------------- But most of you know this stuff anyway. Things aren't so very different with OOP. The primary difference is that, with OOP, the data and the procedures and functions which manipulate the data are combined into a single type, called an object. This concept of combining data and procedures/functions is referred to as encapsulation. Listed below is a way to create an object type called AddressOBJ: TYPE AddressOBJ = object Name: string[20]; Street: string[30]; CityStZip: string[20]; {methods ...} procedure PrintLabel; function Zip: string; end; {object} VAR BobsHouse: AddressOBJ; Notice that the object type declaration is very similar to a record declaration, but the keyword object is used instead of record. The procedure and function declaration are included in the object declara- tion, and are referred to (in OOP nomenclature) as methods. The methods are not passed the address record as a parameter, because the procedure and functions in an object can directly access the data in the object, and so don't need to be passed the data. The "variable" BobsHouse, which is of type AddressOBJ, is referred to as an instance - in other words, object variables are called instances! All object type declara- tions in the Toolkit end with the characters "OBJ". The individual elements of an object (just like a record) can be accessed by using the "dot" notation. For example, to print a label, you would use the following statement: BobsHouse.PrintLabel You can also use the "with identifier do" shortcut, e.g. with BobsHouse do begin PrintLabel ..... end; {with} In theory, you could also access the data elements using the dot method, e.g. BobsHouse.Street := '12 Paradise Drive', but you shouldn't! While it is syntactically correct and will compile, this is bad practice. A basic precept of encapsulation is that you never access Toolkit Basics 3-3 -------------------------------------------------------------------------------- the data elements of an instance directly. You should create methods within the object to access the object's data. In the above example, there is no way to access the data in the object (other than breaking the rules) so a data update method would need to be created. Listed below is an improved type declaration of the AddressOBJ: TYPE AddressOBJ = object Name: string[20]; Street: string[30]; CityStZip: string[20]; {methods ...} procedure Adddata(Nam,St,Ci,St,Zp:string); procedure PrintLabel; function Zip: string; end; {object} The new AddData method would be used to set the values of the Name, Street and CityStZip elements. For example, the following two state- ments would print the label: with BobsHouse do begin AddData('Bobbo','12 Paradise Drive', 'Dome','TX','77186'); Printlabel; end; If you are curious, each method identified in an object must be included in the body of the program or unit. For example, later in the program there might be the following method definition: ... procedure AddressOBJ.Adddata(Nam,St,Ci,St,Zp:string); {} begin Name := copy(Nam,1,20); Street := copy(St,1,30); ... end; {of proc} ... Now, back to the main discussion. The advantage of using methods to access the object data is that the object user (that's you) does not need to know or care about how the data is stored within the object. The internal data structure might be modified at some stage to use 3-4 User's Guide -------------------------------------------------------------------------------- pointers or to store ASCIIZ strings, etc, but the same method AddData can still be called in the same way as previously. What Adddata does (behind the scenes) may have changed, but your program still compiles and runs without modification. For various reasons, many objects need to be initialized before they can be used. For example, some memory may need to be allocated on the heap. When you have finished with such an object, the memory will need to be disposed of. Just because the procedure in which you declared an object instance (i.e. variable) has terminated, it does not necessarily mean that the object data has been removed. Although not mandatory, a de facto OOP standard is to have two special methods for all objects. Namely, INIT and DONE. These special methods are used to initialize an object and dispose of an object, respec- tively. Throughout the Toolkit, every object has INIT and DONE methods. Always call INIT before any other object methods, and always call DONE when you have finished with an object. In some instances the INIT method may require some parameters. In our example, the object is useless if no data has been assigned. Adopting the new convention, it might be more appropriate to rename the AddData method INIT, since you will always want to assign values before calling other object methods. We should also add a DONE method which will provide a way of disposing of any dynamic data we might incorpo- rate at a later stage. The revised object declaration would be as fol- lows: TYPE AddressOBJ = object Name: string[20] Street: string[30] CityStZip: string[20] {methods ...} procedure Init(Nam,St,Ci,St,Zp:string); procedure PrintLabel; function Zip: string; procedure Done; end; {object} For now, the DONE procedure will probably be empty, i.e. it performs no action. This is referred to as an abstract method. The following code fragment shows how to use this revised object: with BobsHouse do begin Init('Bobbo','12 Paradise Drive','Dome','TX','77186'); Printlabel; Done; end; Toolkit Basics 3-5 -------------------------------------------------------------------------------- Note: there is an even more compelling reason to have INIT and DONE methods. One of the most powerful features of OOP is polymorphism, which allows you to declare virtual methods (discussed in part 2). In general, objects which use virtual methods must have a constructor and a destructor, and these are normally called INIT and DONE. To recap, the following key points should be remembered: q All Toolkit objects end with the characters OBJ. q Use the "dot" notation to access object methods. q When using the Toolkit always initialize an instance with INIT, and dispose of it with DONE. Having covered the OOP basics as they relate to the Toolkit, it is worthwhile reviewing the message demo program that was discussed in chapter 2: program DemoMessage1; {demms1} Uses DOS, CRT, totFAST, totMSG; Var MsgWin : MessageOBJ; begin Screen.Clear(white,'°'); {paint the screen} with MsgWin do begin Init(1,' Message '); AddLine(''); AddLine('The message unit provides a'); AddLine('very easy way of displaying'); AddLine('pop-up messages in a move-'); AddLine('able window.'); AddLine(''); Show; Done; end; end. Hopefully, the statements make more sense now! 3-6 User's Guide -------------------------------------------------------------------------------- "So what?" and "Why bother?" you may ask. It is not appropriate, at this stage, to explore the advantages of encapsulation. Just use the Toolkit a little, and you will see what can be done with OOP! We have covered everything you need to know about OOP to use the Toolkit (and then some), but as a programmer it is always good to explore "under the hood". A great way to learn practical insights into OOP is to explore the Toolkit source code. When you are ready to extend the Toolkit to meet your specific needs, refer to Part 2. Also, read some of the OOP literature in magazines and books - OOP is here to stay. Meanwhile, be happy with the "how" for now. The "why" will become all too clear in time. Building the Toolkit TPUs The file TOTBUILD.PAS is an "empty" file which USES all the Toolkit units. Its sole purpose is to provide an easy way of re-compiling the Toolkit units. If you ever need to rebuild all the TPUs, load the TOTBUILD.PAS file from the C:\TURBO\TOT directory (or wherever you installed the source files), and select Compile Build. If the program does not successfully compile, refer to the problems section at the end of chapter 2. Compiler Directives Every unit in the Toolkit has an include statement to include the file TOTFLAGS.INC. This file is designed to specify all the common compiler directives that you want to apply to every unit in your program. The compiler directives added to your main program apply only to that module and not to any of the units used by the program. So if you wanted to ensure that a specific compiler directive is operative in every unit, you would have to edit every unit source code file and add the appropriate directive. (Yes, you could add them to the IDE options menu, but if you work on multiple projects, you would have to keep swapping the directives in and out as necessary.) Whenever you develop a program using the Toolkit, you should add an include directive to your main program and any other units you develop, as follows: {$I TOTFLAGS.INC} Any compiler directives you set in the file TOTFLAGS.INC file will then affect your code and the Toolkit code. Toolkit Basics 3-7 -------------------------------------------------------------------------------- The TOTFLAGS.INC file contains a host of standard Turbo Pascal compiler directives which are controlled with four special compiler directives: FINAL, OVERLAY, FLOAT and FLOATEM. These four compiler directives are located at the top of the file in an enabled or disabled state. For example, in the disabled state the $DEFINE directive is broken up with spaces, e.g. { $ DEFINE OVERLAY} To activate a directive simply remove the leading spaces, e.g. {$DEFINE OVERLAY} To disable the directive again just put the spaces back! The four compiler directives have a significant impact on the final program code, and are discussed in detail below. FINAL During program development it is a good idea to switch on range check- ing and stack checking, etc. These directives keep you honest and reduce the likelihood of a machine "hang" or lock-up. For example, if you try to assign a value of 300 to a byte, the compiler will identify the problem before it goes too far. The bad news is that programs compiled in the check everything state tend to be slower and larger than their carefree brethren. So, once you have debugged and tested your program and you are confident that the checking is no longer nec- essary, you should switch off the appropriate directives. The FINAL compiler directive is designed to simplify this task. During program development, you should disable the FINAL compiler directive, and then enable it when you are ready to build and distribute your production program. At the time of printing, the following compiler directives are influenced by the FINAL directive: FINAL enabled {$S-} no stack checking {$R-} no range checking {SD-} no debug information {$L-} no local symbols FINAL disabled {$S+} stack checking on {$R+} range checking on {SD+} debug information on {$L+} local symbols on 3-8 User's Guide -------------------------------------------------------------------------------- When FINAL is disabled, another Toolkit directive is defined: CHECK. When CHECK is enabled, the Toolkit uses some additional code to check parameters passed to methods. For example, in the WINOBJ method SetSize, a check is made to ensure the window coordinates fit on the screen. When the FINAL directive is enabled, CHECK is automatically disabled to reduce program size. If a program runs fine until you set the FINAL directive, check the parameters you are using in the problem area of the code. Remember that if you want to use the IDE or stand alone debugger on your application, you must disable the FINAL directive. OVERLAY Any of the Toolkit units can be overlaid. Turbo Pascal requires that all overlaid units include a {$O+} compiler directive, and that all methods, procedures and functions use the far call method, i.e. they are compiled in the {$F+} state. By activating the {$DEFINE OVERLAY} statement in the TOTFLAGS.INC file, all the Tool- kit units will be compiled in the {$F+,O+} state. It is important to note that Turbo Pascal imposes an important restric- tion on an overlaid unit - there must be no initialization statements at the end of the unit. Every Toolkit unit has an initialization procedure, and this procedure name is consistently the unit name minus the TOT prefix plus the characters "INIT". For example, the initializa- tion procedures for the TOTFAST and TOTSYS units are FASTINIT and SYSI- NIT, respectively. IMPORTANT: When the OVERLAY compiler directive is activated, you must call each overlay unit's INIT procedure as the first statements in your main program. Listed below is a small demo program, DEMOV1.PAS, which is an overlaid version of the DEMMS1.PAS file reviewed earlier. program DemoOverlay1; {demov1.pas - make sure the OVERLAY compiler directive is enabled in the TOTFLAGS.INC file before compiling} {$I TOTFLAGS.INC} Uses OVERLAY, DOS, CRT, totSYS, totLOOK, totINPUT, totFAST, totWIN, totIO1, totMSG; {$O totSYS} {$O TOTLOOK} {$O totINPUT} {$O totFAST} Toolkit Basics 3-9 -------------------------------------------------------------------------------- {$O totWIN} {$O totIO1} {$O totMSG} Var MsgWin : MessageOBJ; begin OvrInit('DEMOV1.OVR');{initialize the overlay} OvrInitEMS; SYSInit; {initialize the Toolkit units} LOOKInit; INPUTInit; FASTInit; WINInit; IO1Init; MSGInit; Screen.Clear(white,'°'); {paint the screen} with MsgWin do begin Init(1,' Message '); AddLine(''); AddLine('The message unit provides a'); AddLine('very easy way of displaying'); AddLine('pop-up messages in a move-'); AddLine('able window.'); AddLine(''); Show; Done; end; end. Note that all the units used directly or indirectly by the totMSG unit are used and the appropriate INIT procedure is called. The order of the INIT procedure calls is important. If you are using any Toolkit units which write to the screen and poll the keyboard, then you should, as a minimum, call the following procedures in the specified order: SYSInit; LOOKInit; INPUTInit; FASTInit; WINInit; If you execute the DEMOV1 program you will notice it is unbearably slow! As the demo ably illustrates, you should not overlay the primary Toolkit units totSYS, totINPUT, and totFAST. Routines from these units are called very frequently, and placing them in overlays will really slooooow your application down. DEMOV2.PAS, listed below, is a more practical overlay solution: 3-10 User's Guide -------------------------------------------------------------------------------- program DemoOverlay2; {demov2 - make sure the OVERLAY compiler directive is enabled in the TOTFLAGS.INC file before compiling} {$I TOTFLAGS.INC} Uses OVERLAY, DOS, CRT, totSYS, totLOOK, totINPUT, totFAST, totWIN, totIO1, totMSG; {$O totWIN} {$O totIO1} {$O totMSG} Var MsgWin : MessageOBJ; begin OvrInit('DEMOV2.OVR');{initialize the overlay} OvrInitEMS; SYSInit; {initialize the Toolkit units} LOOKInit; INPUTInit; FASTInit; WINInit; IO1Init; MSGInit; Screen.Clear(white,'°'); {paint the screen} with MsgWin do begin Init(1,' Message '); AddLine(''); AddLine('The message unit provides a'); AddLine('very easy way of displaying'); AddLine('pop-up messages in a move-'); AddLine('able window.'); AddLine(''); Show; Done; end; end. In this example, only the three units totWIN, totIO1 and totMSG are overlaid, and performance is almost as good as a non-overlaid version. It is important to note the OVERLAY compiler directive disables all the Toolkit initialization procedures regardless of whether each unit is actually overlaid or not. For this reason, the second demo still ini- tializes all of the Toolkit units. Refer to the Flash cards for a list of unit dependencies, i.e. which files to INIT when you use a unit. Toolkit Basics 3-11 -------------------------------------------------------------------------------- Finally, if you are new to overlays, don't forget that the overlay file (.OVR) can be combined back into the EXE file using the following DOS command: COPY/B progname.EXE + progname.OVR The OvrInit procedure must initialize the overlay file from the EXE file, and this can be achieved with the following command: OvrInit(ParamStr(0)); FLOAT Turbo Pascal and the Toolkit support extended reals. By default, Turbo Pascal uses six byte reals, but using compiler directives high preci- sion Single, Double, Extended and Comp reals can be used. If you want to use the higher precision reals, activate the FLOAT com- piler directive. The Toolkit will automatically set the necessary Turbo Pascal compiler directives. When FLOAT is enabled, the Toolkit supports all real types. When FLOAT is disabled, all the real types are type- cast to the base REAL type. In other words, the types SINGLE, DOUBLE, EXTENDED and COMP appear to the compiler as plain old REAL. This type-casting is performed in the small totREAL unit. If the software compiled in the FLOAT state is to be run on a computer without a math co-processor, the FLOATEM compiler directive (discussed next) must also be enabled. Note that the totSYS unit provides an object for checking the presence of a math co-processor. FLOATEM Turbo Pascal is capable of emulating an 8087 math co-processor if the PC does not have one installed. Enable the FLOAT and FLOATEM (short for float emulation) directives if you want to use high precision reals on PCs with no math co-processor. Note that the use of FLOATEM will increase the size of the program, because Turbo Pascal links in the 8087 emulation code. Only use this directive when necessary. Using Standard Objects Virtually all programs built with the Toolkit perform some basic tasks such as writing to the screen and polling the keyboard. The Toolkit provides a number of object instances (i.e. object variables) that are automatically initialized when you use the related unit. For example, the totFAST unit includes an instance of a ScreenOBJ called SCREEN. 3-12 User's Guide -------------------------------------------------------------------------------- Listed below are each of the instances which provide the basic program- ming facilities: SCREEN The SCREEN instance is of type ScreenOBJ and is declared in the totFAST unit. SCREEN should be used for all writing to the screen. For more details, refer to chapter 5: Writing to the Screen. MOUSE The MOUSE instance is of type MouseOBJ and is declared in the totINPUT unit. The primary purpose of MOUSE is to provide a convenient way of controlling the mouse cursor. The methods HIDE and SHOW control whether the mouse is visible, and the method function VISIBLE returns true if the mouse is on display. Note that the Toolkit automatically hides the mouse while screen writing occurs. For further information, refer to chapter 6: Keyboard and Mouse Input. KEY The KEY instance is of type KeyOBJ and is also declared in the totINPUT unit. This instance provides all the methods for accessing the user's keyboard and mouse input. The main method is Getkey, which waits for the user to press a key or click a mouse button. More details are described in chapter 6. MONITOR^ The MONITOR instance is a pointer to a DisplayOBJ and is declared in the totSYS unit. MONITOR^ can be used to ascertain the width and depth of the display, as well as set condensed mode display on or off. Refer to chapter 4: Determining and Controlling Hardware for more information. Controlling a Program's Look & Feel As well as the global instances described in the last section, the Toolkit includes a variety of object instances which can be used to control the overall look and feel of a program. For example, the SCROLLTOT^ instance controls the way that scroll bars are drawn. To change the look of your application, all you have to do is modify the appropriate instance. To save data space, all these instances are dynamically created on the heap and must be referenced with the pointer symbol (^). For example, to call the SCROLLTOT method SetScrollChars, you would use the following syntax: ScrollTOT^.SetScrollChars(....); Toolkit Basics 3-13 -------------------------------------------------------------------------------- Listed below is a full description of each global instance which con- trols the look and feel of your application. All these instances are automatically initialized by the Toolkit, and a set of more than adequate defaults (!) are assigned. In addition to these instances, there is a byte variable LPTPORT, in the totMISC unit, which is used to indicate the default printer port (set to 0 for LPT1, 1 for LPT2, etc.). Note: the Toolkit uses a single byte to indicate the display attribute or color - that is, the foreground and background bytes combined into a single attribute byte. The totFAST unit provides three functions to help you manipulate color attributes. The CATTR function is passed a foreground and background color and returns a single combined attribute byte. The FATTR function is passed an attribute byte and returns the foreground color component. The BATTR function is passed an attribute byte and returns the back- ground color component. For example, the expression CAttr(white,blue); returns the value of 31, and the expression FAttr(31) returns 15, which is white. In all, there are 256 different foreground and background color combinations; the Flash Cards include a chart which details them all. When assigning colors, be sure to check the Monitor^.ColorOn bool- ean function method to determine whether the system is using a color or a monochrome device. LookTOT The unit totLOOK is designed specifically to provide you with an easy way to change the overall cosmetic appearance of your programs. The unit includes a single instance LookTOT which is a pointer to a LookOBJ object. LookTOT controls the window, menu and list default display characteristics. If you want to change the overall color and style of your program, change the LookTOT settings, and these will be inherited by the window, menu and list objects. Listed below are the methods for setting the various defaults: SetWindow(Border,Body,Icons,Title: byte); 3-14 User's Guide -------------------------------------------------------------------------------- This method sets the display attributes for the window border (where the box is drawn), the central part of the window, the close and zoom characters, and the window title. The defaults depend on whether the host PC is using a color or a monchrome display. SetWinKeys(Move,Stretch,Zoom: word); This method sets the values of the keys which will invoke the window move, stretch and zoom commands. The defaults are [KEYCAP], [KEYCAP], and [KEYCAP]. SetListKeys(Endkey,Esc,Toggle,Tag,UnTag: word); SetListKeys sets the values of the keys which are used with a ListOBJ instance. The EndKey and Esc keys are used to remove the list window, and the defaults are [KEYCAP] and [KEYCAP]. The Toggle key is used to select or deselect individual items in the list and the default is the [KEYCAP]. The Tag and UnTag keys are used to globally select or dese- lect all items in the list, and the defaults are [KEYCAP] and [KEYCAP]. SetListChars(LeftChar,RightChar,ToggleOnChar,ToggleOffChar:char); Sets the display characters which are used to emphasize the highlighted topic in a list, as well as the characters used to indicate whether an item in a list is selected or not. SetMenu(Bor,Tit,Icon,HiHot,HiNorm,LoHot,LoNorm,Off:byte) Sets the display characteristics of the pop-up and pull-down menus. The method is passed eight (count 'em) parameters to give you complete control of all the menu display colors. The first three parameters set the attribute of the menu window border, title and close icon. The next four parameters specify the attributes for the highlighted and normal menu items. (Each menu item may be displayed in two colors to emphasize a specific letter or word.) The last parameter is the attribute for non-selectable (i.e. turned-off) menu items. SetDefaults; Call this method with no parameters if you want to reset all the Look- TOT settings to the Toolkit defaults. Toolkit Basics 3-15 -------------------------------------------------------------------------------- ShadowTOT ShadowTOT is a pointer to an instance of type ShadowOBJ, and is declared in the totFAST unit. ShadowTOT is used to control the size, color and perspective of the shadows used by pop-up windows, lists, menus and the like. An enumerated type ShadowPosition is used to describe the shadow per- spective, and the elements are defined as UpLeft, UpRight, DownLeft, and DownRight. Shadows can be set as see-through or solid characters, the choice is yours! You can even set how wide and deep the shadow will be. The following methods are used to set the shadow characteristics: SetShadowStyle(ShadP:ShadowPosition;ShadA:byte;ShadC:char); The first parameter indicates the direction of the shadow using a mem- ber of the ShadowPosition enumerated type. The second parameter is the display attribute of the shadow, and the third is the character used to draw the shadow. If a space (' ') is passed, the shadow will be see- through, otherwise the shadow will be drawn using the specified charac- ter. SetShadowSize(ShadW,ShadD:byte); This method controls the size of the shadow. Pass the number of charac- ters wide and deep that you want the shadow to be. If the width and depth are set to zero, no shadows will be drawn. SetDefaults; Call this method with no parameters if you want to reset all the Shad- owTOT settings to the Toolkit defaults. The following program, DEMSH1.PAS, illustrates how to change the shadow settings, and figure 3.1 shows the resultant output when the program is executed. program DemoShadow1; {demsh1} Uses DOS, CRT, totFAST; begin with Screen do begin Clear(white,'°'); {paint the screen} ShadFillBox(3,3,20,8,31,1); ShadowTOT^.SetShadowStyle(Upleft,lightgray,' '); 3-16 User's Guide -------------------------------------------------------------------------------- ShadFillBox(3,12,20,19,47,2); ShadowTOT^.SetShadowStyle(Downleft,lightblue,' '); ShadowTOT^.SetShadowSize(4,1); ShadFillBox(35,2,70,10,94,3); ShadowTOT^.SetShadowStyle(DownLeft,white,'!'); ShadFillBox(40,13,60,18,15,4); ShadowTOT^.SetDefaults; ShadFillBox(5,21,75,23,78,5); end; {with} end. Figure 3.1 [SCREEN] The Shadow Demo Program ScrollTOT ScrollTOT is a pointer to an instance of type ScrollOBJ, and is declared in the totFAST unit. ScrollTOT is used to control the charac- ters used to build the scroll bars displayed in scrolling windows. There are basically four different characters used to create a scroll bar. There is the directional arrow at each end, the background charac- ter, and the elevator (or slider) character. These characters are con- trolled with the following two methods: SetScrollChars(U,D,L,R,E,B:char); These six parameters represent the up and down directional arrows on vertical scroll bars, the left and right directional arrows on horizon- tal scroll bars, the elevator character and the background character. SetDefaults; Call this method with no parameters if you want to reset all the ScrollTOT settings to the Toolkit defaults. ScrollTOT does not affect the display attributes of the scroll bars. The window border attribute is normally used. SCREEN provides two methods for writing scroll bars anywhere on the display: WriteHScrollBar, WriteVScrollBar. Refer to Chapter 5 for fur- ther information. Toolkit Basics 3-17 -------------------------------------------------------------------------------- IOTOT Using consistent color schemes is particularly important during full- screen input. The totIO1 unit includes IOTOT which is designed to con- trol all the IO display attributes. IOTOT is a pointer to an object of type InputOBJ. The uses of IOTOT are discussed in detail in chapter 11. FmtNumberTOT The totIO2 unit provides routines for the input of REAL and INTEGER values. These values can be optionally formatted when the user moves to the next field, i.e. when the user is editing, the number is unfor- matted, but when the user exits the field, the number can be formatted in a variety of styles. For example, the number 123456.78 might be formatted as $123,456.78. The totIO2 unit includes FmtNumberTOT, a pointer to an object instance of type FmtNumberOBJ. This instance defines the default formatting that will be applied to real and integer fields during full-screen input. Any of the format defaults can be overridden for individual fields. Field formatting is discussed in detail in chapter 11: Controlling User Input. DateTOT The totDATE unit provides functions for manipulating and converting Julian and Gregorian dates. A number of default values, e.g. the char- acter used to separate the month from the day and the year (like ' / / '), are defined in DateTOT, a pointer to an object instance of type DateOBJ. These defaults are used in both the totDATE and totIO2 units. Refer to chapter 13: Managing Dates for further details. AlphabetTOT The totINPUT unit includes the object AlphabetOBJ for managing upper- and lower-case characters, and the global instance AlphabetTOT is a pointer to an AlphabetOBJ object. AlphabetTOT controls how the Toolkit determines/changes the case of alpha-characters. It is designed for international Toolkit users who want to use a non-English alphabet. If you are writing "English- speaking" programs, you do not need to tamper with this object. The object contains a list of all valid upper-case letters, lower-case letters and punctuation characters. The totLOOK unit includes the type declaration CharSet, which is equal to a set of char. Some methods are 3-18 User's Guide -------------------------------------------------------------------------------- passed a parameter of type CharSet. You may specify multiple characters and/or ranges of characters, provided that they are enclosed in square brackets []. For example, the following expressions are valid CharSets: ['a','b','f'..'z'] ['A'..'Z',#129..#148] ['a'..'z'] [',','.'] AlphabetTOT methods can be called using the syntax AlphabetTOT^.method, and the following methods are supported: SetUpper(Letters:CharSet); Defines which characters comprise the upper-case alphabet. The method is passed one parameter of type CharSet, e.g. SetUpper(['A'..'Z']);. SetLower(Letters:CharSet); Defines which characters comprise the lower-case alphabet. The method is passed one parameter of type CharSet, e.g. SetLower(['a'..'z']);. SetPunctuation(Letters:CharSet); Defines which characters are used in normal punctuation. The method is passed one parameter of type CharSet, e.g. SetPunctua- tion([',',';','.',':',' ']);. IsUpper(K:word): boolean; This function method is passed one parameter, representing the ordinal value of a character, and returns true if the character is listed in the upper-case alphabet, e.g. IsUpper(ord('A'));. IsLower(K:word): boolean; This function method is passed one parameter, representing the ordinal value of a character, and returns true if the character is listed in the lower-case alphabet, e.g. IsLower(107);. IsLetter(K:word): boolean; This function method is passed one parameter, representing the ordinal value of a character, and returns true if the character is listed in either the upper- or lower-case alphabet. IsPunctuation(K:word): boolean; Toolkit Basics 3-19 -------------------------------------------------------------------------------- This function method is passed one parameter, representing the ordinal value of a character, and returns true if the character is listed as a punctuation character. On occasion, the Toolkit needs to convert the case of alpha characters. To accommodate international users, the Toolkit uses AlphabetTOT to convert character case, rather than rely on the English-specific rou- tines provided with Turbo Pascal. The Toolkit calls the following two methods to convert character case: GetUpCase(Ch:char):char; This method is passed a character, and returns the upper-case equiva- lent of the character. GetLoCase(Ch:char):char; This method is passed a character, and returns the lower-case equiva- lent of the character. You can control precisely how characters are converted by creating your own conversion routines. All you have to do is create two functions following some specific rules, and then call the methods Alphabet- TOT^.AssignUpCaseFunc and AlphabetTOT^.AssignLoCaseFunc to instruct the Toolkit to use your functions. For a function to be eligible as a character case converter it must adhere to the following three rules: Rule 1 The function must be declared as a FAR function. This can be achieved by preceding the function with a {$F+} compiler directive, and following the function with a {$F-} direc- tive. Alternatively, Turbo 6 users can use the new keyword FAR following the procedure statement. Rule 2 The function must be declared with one passed parameter of type char, and it must return a char. Rule 3 The function must be at the root level, i.e. the function cannot be nested within another procedure or function. The following function declaration follows these rules: {$F+} function MyUpConverter(Ch:char):char; .....{statements} end; {$F-} 3-20 User's Guide -------------------------------------------------------------------------------- Internally, these functions should check the value of the passed char- acter, and return the character converted to the appropriate case. If the character is not suited to conversion, e.g. '1', simply return the character that was passed. Once you have created two functions (one for upper-case conversion and one for lower-case conversion), you should call the following assignment methods to instruct AlphabetTOT to use your routines: AssignUpCaseFunc(Func:CaseFunc); This method is passed the name of the function used to convert charac- ters to upper case. The procedure must adhere to the rules outlined above. AssignLoCaseFunc(Func:CaseFunc); This method is passed the name of the function used to convert charac- ters to lower case. The procedure must adhere to the rules outlined above. In summary, AlphabetTOT provides you with complete control over how the Toolkit determines and changes the case of alpha characters. Program Size A constant battle during the development of the Toolkit was to minimize the size of programs developed with the Toolkit. A valid criticism of OOP is that it tends to swell the final program size, which makes it a subject of concern to most developers (that's you folks). With a hierarchical object organization even small applications tend to attract a high code overhead. However, the more features of the Toolkit you use, the more efficient your program will be. For example, listed below are the EXE file sizes of three of the demo programs: DemFM8 114k DemDR1 92k DemMS1 56k The combined file size of the three programs is 262k. However, if these three examples are combined into a single example, the file size is actually 141k. The reason for the reduction is that each of the small programs uses common routines which can be shared in the combined program. Toolkit Basics 3-21 -------------------------------------------------------------------------------- In real life, your fledgling program will start out bigger than you expected, but it will grow much less than anticipated as your program expands. Remember that compiler directives have a significant impact on program size, and you should enable the FINAL compiler directive for the production build of your application. If EXE file size is your concern, rather than code size, consider com- pressing the EXE file with a compression utility like PKlite from PKWare, Inc., or LZEXE by Fabrice Bellard.