home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-02-10 | 83.6 KB | 2,168 lines |
- Managing
- Lists
-
-
- "Three questions are essential... What is the author's object? How far
- has he accomplished it? How far is that object worthy of approbation?
-
- Nathanial P Willis
-
-
-
- Introduction
- Lists, lists, lists. Our lives and our software are often dominated by
- lists. The Toolkit provides three powerful units to help you manage
- software lists. (Your lives are your own responsibility!)
-
- In this chapter, the totLINK and totLIST units are discussed in detail.
- totLINK provides objects to help you maintain and sort large lists, and
- totLIST provides objects for displaying lists and arrays in windows.
- A common form of software list is a file directory list. The Toolkit
- provides two flexible directory displaying objects, and these are dis-
- cussed in the next chapter.
-
-
-
- Array and Linked List Theory
- One-dimensional arrays provide a very quick and convenient way of
- managing lists. An array is a data structure designed to store data
- about a fixed number of identical elements. For example, the following
- variable could be used to store the address of my two sisters' and
- three brothers' addresses:
-
- Addresses: array [1..5] of string;
- Data in the array is accessed by specifying the element number. For
- example, the following statement would update my second sister's
- address:
-
- Addresses[2] := "38 The Ridgeway, Friern Barnet, London"
-
- Arrays are very easy to create, but suffer from two major weaknesses:
-
- q The size of an array is fixed at compile time. In other words, in
- your source code you must explicitly state the size (or bounds)
- of the array. In our simple example, this was not a problem
- because I have only five siblings. Imagine, however, that you
- need to write a generic name and address program. You don't know
- how many names and addresses will be required, but you must spec-
- ify the number in your program. The typical solution is to choose
- a large number of elements (e.g. 200) and hope that the user
- doesn't need more. Not only does this set a limit on the pro-
- gram's capacity, it also wastes memory if the maximum number of
-
- 9-2 User's Guide
- --------------------------------------------------------------------------------
-
- elements are not used. Turbo Pascal automatically allocates the
- memory for the entire array regardless of how many elements are
- actually used. Which leads us to the second shortcoming.
-
- q The maximum size of a program's data segment in Turbo Pascal is
- 64k. This means that the sum of all the program's global vari-
- ables cannot exceed 64k, and, therefore, no individual data
- structure can exceed 64k. In our example, each element of the
- array consumed 256 bytes, so 256 of these elements would consume
- the entire data segment leaving no room for other global vari-
- ables.
-
-
- Linked lists do not suffer from these shortcomings. With a linked list,
- each element is stored independently. The first element in the list
- includes a pointer indicating where the second element in the list is
- stored. The second element in the list includes a pointer to the loca-
- tion of the third element in the list, and so on. When you reach an
- element in the list that points to NIL, you know you have reached the
- end of the list. You do not need to fix the maximum number of elements
- in the list at compile time. Furthermore, the data is stored on the
- heap (not in the data segment) and so is not limited to 64k. The list
- can use all of the available memory.
- Although linked lists are very flexible, they can prove complicated to
- program and manage. Fortunately, the Toolkit provides a very powerful
- object which makes managing linked lists a breeze.
-
-
-
- Managing Linked Lists
- The totLINK unit includes the object DLLOBJ. This object provides a
- variety of methods for creating and managing linked lists. If you are
- interested, DLLOBJ actually maintains a doubly-linked list. Each ele-
- ment in the list points to the next element and the previous element.
- This is more efficient when you need to move up and down the list.
-
- The DLLOBJ object is designed to store raw data, i.e. untyped vari-
- ables. This allows you to store literally any data in a DLLOBJ object.
- It is important to note that DLLOBJ is an abstract object and you
- should not create any instances of type DLLOBJ.
- In this chapter, we will concentrate on a descendant object, StrDLLOBJ,
- designed specifically to manage lists of strings. If you want to create
- objects which manage other data types, all you have to do is create an
- object descended from DLLOBJ. In part 2: Extending the Toolkit, there
- is a chapter describing how to create other descendants of DLLOBJ to
- manage specific data types. By way of an example, it describes how to
- create a linked list object for managing records.
-
-
-
- Managing Lists 9-3
- --------------------------------------------------------------------------------
-
- Before exploring the StrDLLOBJ methods, you need to understand some
- doubly-linked-list jargon. Each element in the list is referred to as a
- node. Behind the scenes, the Toolkit keeps track of the location of the
- first node in the list, the last node in the list, and the node that
- was last accessed. The last-accessed node is referred to as the active
- node.
-
-
- Creating a List
-
- To create a linked list, you must declare an instance of type StrDLLOBJ
- and then call the Init and Add methods. The syntax of these methods is
- as follows:
-
- Init;
-
- This method initializes the object and must be called before the other
- methods.
-
- Add(Str:string): integer;
-
- This function method creates a new node in the linked list and stores
- it in the string Str. The node is actually created immediately follow-
- ing the active node. Once the node has been added, the active node is
- moved to point to it. The function returns an integer code - a 0
- indicates success, a 1 indicates there was insufficient memory to
- create the node, and a 2 indicates there was enough memory to create
- the node, but too little memory to store the string.
-
- Ordinarily, to populate a list, all you have to do is call Init, and
- then make multiple calls to Add. Listed below is an extract of the demo
- program, DEMLK1.PAS, which stores a list of names in a linked list.
-
- var
- GirlFriends: StrDLLOBJ;
- Retcode: integer;
- begin
- with GirlFriends do
- begin
- Init;
- Retcode := Add('Erica');
- if Retcode <> 0 then exit;
- Retcode := Add('Theresa');
- if Retcode <> 0 then exit;
- Retcode := Add('Lynn');
- if Retcode <> 0 then exit;
- Retcode := Add('Donna');
- if Retcode <> 0 then exit;
-
-
-
-
- 9-4 User's Guide
- --------------------------------------------------------------------------------
-
- Retcode := Add('Godzilla');
- if Retcode <> 0 then exit;
- {....}
- end;
- end.
-
-
- To save checking the value of Retcode every time you add a node, you
- might check that there is enough memory available for all the nodes,
- and ignore the return code, as follows:
- var
- GirlFriends: StrDLLOBJ;
- Retcode: integer;
-
- begin
- with GirlFriends do
- begin
- Init;
- if maxavail > 200 then
- begin
- Retcode := Add('Erica');
- Retcode := Add('Theresa');
- Retcode := Add('Lynn');
- Retcode := Add('Donna');
- Retcode := Add('Godzilla');
- {...}
- end;
- end;
- end.
-
- In fact, if you are using Turbo Pascal 6.0 and the extended compiler
- directive {$X+} has been set, you can ignore the function return value,
- as follows:
-
- {$X+}
- var
- GirlFriends: StrDLLOBJ;
- begin
- with GirlFriends do
- begin
- Init;
- if maxavail > 200 then
- begin
- Add('Erica');
- Add('Theresa');
- Add('Lynn');
- Add('Donna');
- Add('Godzilla');
-
-
-
- Managing Lists 9-5
- --------------------------------------------------------------------------------
-
- {...}
- end;
- end;
- end.
-
-
- Maintaining a List
-
- Once you have created a list, the following methods can be used to
- navigate the list:
-
- TotalNodes: longint;
-
- This function method returns a longint identifying the total number of
- nodes in the list.
-
- ActiveNodeNumber: longint;
-
- Returns the position of the active node.
-
- Jump(NodeNumber: longint)
-
- This method moves the ActiveNodePtr to the specified node number. Add
- inserts a node after the active node pointer. So, to insert a node
- anywhere in the list, call the method Jump to move the active node
- pointer and then call Add.
-
- Advance(Amount: longint);
-
- Moves the active node down the list the specified number of nodes. If
- there are insufficient nodes, the active node pointer is moved to the
- end of the list.
-
- Retreat(Amount: longint);
-
- Moves the active node up the list the specified number of nodes. If
- there are too few nodes, the active node pointer is moved to the begin-
- ning of the list.
-
-
-
- Advanced List Management
-
- In many programs, all you'll need to do is create a list, add items to
- the list, and then display the list using one of the browse or list
- displaying objects (discussed later). However, if you want to delete
- items from the list, swap the positions of two items in the list, or
- change an item in the list, you will need to access the nodes in the
- list directly.
-
-
- 9-6 User's Guide
- --------------------------------------------------------------------------------
-
- Each node in the list is actually a DLLNodeOBJ object, and many of the
- list manipulation methods access a pointer to the node known as a
- DLLNodePtr.
-
- Without worrying about the details, all you need to know is how to
- determine the value of DLLNodePtr for any given node. The following
- four function methods serve this purpose:
-
- NodePtr(NodeNumber:longint): DLLNodePtr;
-
- Pass this function a node number, and it will return a pointer to the
- node.
-
- ActiveNodePtr: DLLNodePtr;
-
- Returns a pointer to the active node.
-
- StartNodePtr: DLLNodePtr;
-
- Returns a pointer to the first node in the list.
-
- EndNodePtr: DLLNodePtr;
-
- Returns a pointer to the last node in the list.
-
- Having established the DLLNodePtr value, the following methods can be
- used to manipulate the node data:
-
-
- Change(Node:DLLNodePtr;Str:string):integer;
- Changes the value at the node to Str. This function method returns an
- integer to indicate whether the change was a success: 0 indicates suc-
- cess, 2 indicates insufficient memory for the data, and 3 indicates an
- invalid node pointer.
-
-
- DelNode(Node:DLLNodePtr);
- Deletes the node.
-
-
- SwapNodes(Node1,Node2:DLLNodePtr);
- Swaps the data assigned to the two nodes.
-
-
- InsertBefore(Node:DLLNodePtr; Str:string);
-
-
-
-
- Managing Lists 9-7
- --------------------------------------------------------------------------------
-
- Inserts a new node immediately before the specified node. (The Add
- method inserts the data after the active node.) This method provides a
- way of inserting a node at the very beginning of the list. After this
- method has been called, the active node pointer is set to the first
- node in the list.
-
-
- GetStr(Node:DLLNodePtr; Start,Finish:longint): string;
- This function returns a sub-string of the data stored at the specified
- node. The parameters Start and Finish indicate the starting and ending
- characters to be extracted in the string. For example, values of 6 and
- 10 will return five characters commencing with the character in posi-
- tion 6 of the string. Pass values of zero for the Start and Finish
- parameters, if you want the entire string.
-
-
- Sort(SortID: shortint; Ascending: boolean);
- This powerful method rearranges the order of all the nodes in a list,
- i.e. it sorts the list. The sort routine is generic and can be utilized
- by any descendant of the DLLOBJ object. In the case of StrDLLOBJ, the
- first parameter has no meaning, and should be set to one. If you want
- the list sorted in ascending order (i.e. lowest first), pass TRUE as
- the second parameter, otherwise pass FALSE.
-
-
- Listed below is the demo program DEMLK2.PAS which illustrates many of
- the methods discussed thus far. Figure 9.1 shows the generated output.
- Program DemoLinkTwo;
- {DEMLK2 - creating a linked list}
-
- Uses CRT,
- totLink;
- var
- GirlFriends: StrDLLOBJ;
- Retcode: integer;
- I : integer;
-
- begin
- ClrScr;
- with GirlFriends do
- begin
- Init;
- if maxavail > 200 then
- begin
- Retcode := Add('Erica');
- Retcode := Add('Theresa');
- Retcode := Add('Lynn');
- Retcode := Add('Donna');
- Retcode := Add('Godzilla');
-
-
- 9-8 User's Guide
- --------------------------------------------------------------------------------
-
- writeln('Total nodes: ',TotalNodes);
- writeln('Active node number: ',ActiveNodeNumber);
- for I := 1 to TotalNodes do
- writeln(GetStr(NodePtr(I),0,0));
- writeln;
- RetCode := InsertBefore(NodePtr(4),'Joyce');
- writeln('Total nodes: ',TotalNodes);
- writeln('Active node number: ',ActiveNodeNumber);
- SwapNodes(NodePtr(5),NodePtr(2));
- Retcode := Change(StartNodePtr,'Ekka');
- for I := 1 to TotalNodes do
- writeln(GetStr(NodePtr(I),0,0));
- writeln;
- writeln('Sorted:');
- Sort(1,true);
- for I := 1 to TotalNodes do
- writeln(GetStr(NodePtr(I),0,0));
- end;
- end;
- end.
-
-
- Figure 9.1 [SCREEN]
- Using
- StrDLLOBJ
-
-
-
- Removing a List
- Calling the method Done will always dispose of all the memory consumed
- by a list. A list can also be emptied with the following method:
-
-
- EmptyList;
- Removes all the memory allocated on the heap by chaining back through
- the list and disposing of each node. The ActiveNodePtr, StartNodePtr
- and EndNodePtr are set to nil, and TotalNodes is set to nil.
-
-
-
- Comparing StrDLLOBJ to Arrays
- If you have never used linked lists before, but you have used arrays,
- you might be interested in comparing array commands with StrDLLOBJ com-
- mands. Table 9.1 compares the command syntax by manipulating a list of
- 5 names.
-
-
- StrDLLOBJ String Array
-
-
-
- Managing Lists 9-9
- --------------------------------------------------------------------------------
-
- Declaring the Variable or Instance
- var NameList: StrDLLOBJ; var NameList: array[1..5] of
- string;
-
- Initializing the List
- NameList.Init; fillchar(NameList,sizeof(NameLis
- t),#0);
- Populating the List
- with NameList do NameList[1] := 'Mark';
- begin NameList[2] := 'Kirk';
- Retcode := Add('Mark'); NameList[3] := 'Lloyd';
- Retcode := Add('Kirk'); NameList[4] := 'Charles';
- Retcode := Add('LLoyd'); NameList[5] := 'Keith';
- Retcode := Add('Charles');
- Retcode := Add('Keith');
- end;
-
- Writing an item to the display
- with NameList do writeln(NameList[3]);
- writeln(GetStr(NodePtr(3),0,0));
-
- Swapping two items
- with NameList do Temp := NameList[4];
- SwapNodes(NodePtr(4),NodePtr(5)); NameList[4] := NameList[5];
- NameList[5] := Temp;
-
- Sorting the List
- NameList.Sort(1,true); Ugh!
-
- Erasing the Entries
- NameList.EmptyList; fillchar(NameList,sizeof(NameLis
- t),#0);
-
-
-
- Don't forget that the StrDLLOBJ is much more memory efficient, and is
- not limited to a set number of elements.
-
- Much of the power of DLLOBJ is not realized until you create descendant
- objects, customized for your specific needs. Refer to Part 2: Extending
- The Toolkit for further details.
-
-
- Browse Lists
-
- Whether your data is in a DLLOBJ (or descendant) object, or in a string
- array, you will want to take advantage of the Toolkit's objects for
-
-
-
- 9-10 User's Guide
- --------------------------------------------------------------------------------
-
- automatically displaying lists in windows. In this section, the Brow-
- seOBJ family of objects will be discussed. These objects display lists
- in a scrollable window.
-
-
- Note that a browse window does not include a highlight bar. The
- user browses the contents of the list, but does not select an
- individual element. The ListOBJ family (discussed in the next sec-
- tion) provides objects for highlighting individual elements in a
- list.
-
-
-
- Figure 9.2 shows the object hierarchy for the BrowseOBJ family. The
- base object BrowseOBJ is abstract, and you should not create any
- instances of this type. The BrowseArrayOBJ object displays the contents
- of a string array, and the BrowseLinkOBJ displays the contents of an
- object in the DLLOBJ family. Many programs need to display the contents
- of ASCII files, and the object BrowseFileOBJ makes this a snap.
-
- Figure 9.2 [PICTURE]
- BrowseOBJ
- Object Hierarchy
-
-
-
-
- Common Methods
- The totLOOK unit includes an object LookTOT which is used to control
- the overall look and feel of applications developed with the Toolkit.
- Call the method LookTOT^.SetListKeys to change the keys which the user
- can press to close the browse window. The defaults are [KEYCAP] and
- [KEYCAP]. Refer to page 3-12 for further details.
-
- Because all the objects are derived from the BrowseOBJ, they share a
- set of common methods. The following methods can be used with any of
- the browse objects:
-
- SetTopPick(TopPick: longint);
-
- This method identifies the node number of the first node that will be
- displayed in the list window.
-
- SetStartCol(Column: longint);
-
- This method identifies the character position of the first character of
- the string which is visible in the list window.
-
-
-
-
- Managing Lists 9-11
- --------------------------------------------------------------------------------
-
- SetEndCol(Column: longint);
-
- This method sets the maximum lateral scrolling by identifying the last
- column which will be displayed when the window is scrolled to the right
- maximum. All lateral scrolling can be disabled by calling the Stretch-
- WinOBJ method SetScrollable (discussed later).
-
- Show;
-
- This method displays the list and immediately returns control back to
- the calling procedure, i.e. the Toolkit does not wait for user input.
- This option is useful if you want to display the list on the screen,
- but not activate it.
-
- Go;
-
- This method is the main "do it" method. If the list is not on display,
- it is drawn. The Toolkit then processes user input until an end key is
- pressed.
-
- LastKey:word;
-
- LastKey returns the key code of the key the user pressed to end the
- browse session. This is used if you need to know whether the user
- escaped or pressed the finish key.
-
- Done;
-
- As always, Done disposes of the Browse window object, and should always
- be called when the object is no longer required.
-
-
- Modifying the Window Characteristics
-
- The list data is displayed in a window of type StretchWinOBJ. You can
- modify any window attribute by directly calling the StretchWinOBJ meth-
- ods. BrowseOBJ includes a function method, Win, which returns a pointer
- to the StretchWinOBJ instance. To modify the window, call the
- appropriate StretchWinOBJ method using the following syntax:
- [BrowseOBJInstance].Win^.method
-
- For example, the following statements would change the default window
- size, style and title:
- var MyBrowser: BrowseLinkOBJ;
-
- begin
- with MyBrowser do
- begin
-
-
- 9-12 User's Guide
- --------------------------------------------------------------------------------
-
- Init;
- Win^.SetSize(1,1,50,20,3);
- Win^.SetTitle('My Browser');
- end;
- end.
-
-
- All the display colors used by the browse objects are controlled by the
- underlying window attributes. To change the browse display colors call
- the window method SetColors, e.g.
- Win^.SetColors(23,23,31,30);
-
-
- Call Win^ methods to set all the browse window characteristics, e.g.
- colors, title, remove status, boundaries, etc. Refer to chapter 7:
- Using Windows for a thorough discussion of the StretchWinOBJ methods.
-
-
- Browsing Arrays
-
- The BrowseArrayOBJ object provides a very simple way to browse the
- contents of a string array. In addition to the common methods just
- described, there is another method as follows:
-
- AssignList(var StrArray; Total:longint; StrLength:byte);
-
- AssignList identifies the string array that will be displayed. The
- three parameters are the string array, the total number of elements in
- the array, and the length of each string in the array. The string
- length parameter must reflect the string length of the array when it
- was declared, not the maximum length of any string assigned to the
- array.
-
- For example, if a string array was declared as follows:
-
- MyGirls: array[1..54] of string;
- then the list is assigned with the following calls:
-
- with MyBrowser do
- begin
- Init;
- AssignList(MyGirls,54,255);
- {...}
- end;
-
-
-
- Managing Lists 9-13
- --------------------------------------------------------------------------------
-
- Having assigned the list, all you need to do is call Show and the
- browse window will pop onto the display. Listed below is a the demo
- program DEMBR1, followed by figure 9.3 illustrating the resultant dis-
- play.
-
- Program DemoBrowseArrayOne;
- {SEMBR1}
- Uses DOS,CRT,
- totINPUT, totFAST,totLIST, totSTR;
-
- var
- BWin: BrowseArrayOBJ;
- StringList: array[1..26] of string[100];
- I : integer;
- begin
- for I := 1 to 26 do {first assign something to the string array}
- StringList[I] := 'Line '+
- IntToStr(I)+': '+
- replicate(80,char(I+64));
- Screen.Clear(white,'░'); {paint the screen}
- Key.SetFast;
- Key.SetClick(true);
- with BWin do
- begin
- Init;
- AssignList(StringList,26,100);
- Go;
- Done;
- end;
- end.
-
-
- Figure 9.3 [SCREEN]
- Browsing an
- Array
-
-
- The demo program DEMBR2.PAS, listed below, is very similar to
- DEMBR1.PAS. The only difference is that two Win^ methods are called to
- modify the default window settings. Figure 9.4 shows the impact on the
- display.
- Program DemoBrowseArray;
-
- Uses DOS,CRT,
- totFAST,totLIST, totSTR;
-
-
-
- 9-14 User's Guide
- --------------------------------------------------------------------------------
-
- var
- BWin: BrowseArrayOBJ;
- StringList: array[1..26] of string[100];
- I : integer;
- begin
- for I := 1 to 26 do {first assign something to the string array}
- StringList[I] := 'Line '+
- IntToStr(I)+': '
- +replicate(80,char(I+64));
- Screen.Clear(white,'░'); {paint the screen}
- with BWin do
- begin
- Init;
- AssignList(StringList,26,100);
- Win^.SetSize(30,5,50,15,2);
- Win^.SetTitle('Array Browse Demo');
- Go;
- Done;
- end;
- end.
-
-
- Figure 9.4 [SCREEN]
- Modifying the
- Browse Window
-
- Browsing Linked Lists
- The BrowseLinkOBJ is very similar to the BrowseArrayOBJ, the primary
- difference being that BrowseLinkOBJ displays the data stored in a
- DLLOBJ object rather than in an array.
-
- In addition to the common methods described, there are two additional
- methods as follows:
-
- AssignList(Var LinkList: DLLOBJ);
-
- This method is passed a DLLOBJ instance, or any instance of an object
- derived from DLLOBJ, e.g. StrDLLOBJ.
-
- ListPtr: DLLPtr;
-
- This method returns a pointer to the DLLOBJ used to create the list.
- You can access the linked list by using the syntax MyBrow-
- ser.ListPtr^.method.
-
- Listed below is the program DEMBR3.PAS, which illustrates how to build
- a large list of 500 items and display it in a browse window. Figure 9.5
- shows the browse display.
-
-
-
- Managing Lists 9-15
- --------------------------------------------------------------------------------
-
- Program DemoBrowseList;
-
- Uses DOS,CRT,
- totFAST, totLINK, totLIST, totSTR;
- var
- BWin: BrowseLinkOBJ;
- LL : StrDLLOBJ;
-
- procedure CreateLinkedList;
- {}
- var
- I, Retcode : integer;
- begin
- with LL do
- begin
- Init;
- for I := 1 to 500 do
- Retcode := Add('This is line '+IntToStr(I)+
- ': '+replicate(200,char(random(255))));
- end; {with}
- end; {CreateLinkedList}
- begin
- Screen.Clear(white,'░'); {paint the screen}
- CreateLinkedList;
- with BWin do
- begin
- Init;
- AssignList(LL);
- Win^.SetTitle('List Browse Demo');
- Go;
- Done;
- end;
- LL.Done
- end.
-
-
- Figure 9.5 [SCREEN]
- Browsing a
- StrDLLOBJ
-
-
- Browsing Files
-
- The BrowseFileOBJ object makes browsing ASCII text files easy. Browse-
- FileOBJ is descended from BrowseOBJ and inherits all the associated
- methods. In addition, the following method AssignFile is used to
- instruct the object on which file to display:
-
-
-
- 9-16 User's Guide
- --------------------------------------------------------------------------------
-
- AssignFile(Filename:string): integer;
-
- This function method is passed a string indicating the name of the file
- to be browsed. Behind the scenes, this function loads the file from
- disk into a temporary StrDLLOBJ object. The function will return a 0 if
- the file was successfully loaded. A return value of 1 indicates that
- the file was not found, and so cannot be displayed. A return value of 2
- indicates that there wasn't enough memory to load the entire file. In
- such an instance you may want to use a MessageOBJ to advise the user
- that only part of the file can be browsed.
-
- That's all there is to it! Just declare a BrowseFileOBJ instance and
- call the methods Init, AssignFile and Go:
-
- var FileWin: BrowseFileOBJ;
- begin
- with FileWin do
- begin
- Init;
- if AssignFile('Help.TXT') = 0 then
- Go;
- Done;
- end;
- end.
-
- The following method can be used to directly access the linked list
- created by the BrowseFileOBJ instance:
-
-
- ListPtr: StrDLLPtr;
- This method returns a pointer to the StrDLLOBJ used to create the list.
- You can access the linked list by using the syntax MyFile.ListPtr^.me-
- thod.
-
-
- Listed below is the example program, DEMBR4.PAS. This small program
- will display the contents of either a file specified on the command
- line when the program was executed, or (if no parameters were speci-
- fied) the contents of c:\autoexec.bat.
-
-
- Program DemoBrowseFile;
- Uses DOS,CRT,
- totFAST, totINPUT, totLINK, totLIST, totSTR;
- const
- DefaultFile = '\autoexec.bat';
- var
-
-
-
- Managing Lists 9-17
- --------------------------------------------------------------------------------
-
- BWin: BrowseFileOBJ;
- RetCode: integer;
- Filename: string;
-
- begin
- Screen.Clear(white,'░'); {paint the screen}
- if ParamCount = 0 then
- FileName := DefaultFile
- else
- FileName := ParamStr(1);
- with BWin do
- begin
- Init;
- Retcode := AssignFile(Filename);
- if Retcode in [0,2] then
- Go
- else
- Writeln('Unable to load file: ',Filename,'.');
- Key.DelayKey(2000);
- Done;
- end;
- end.
-
-
-
- 9-18 User's Guide
- --------------------------------------------------------------------------------
-
- Selection Lists
-
- So far, this chapter has focused on browse windows, where the user can
- simply scroll data vertically and horizontally. By contrast, a list
- window is designed to display selectable items, much like a menu. One
- of the items within the list window is always highlighted, and the list
- may be organized into single or multiple columns. The scroll bars are
- used to move the item highlight bar. The list is removed when the user
- presses an end key such as [KEYCAP] or [KEYCAP], when the user presses
- [KEYCAP], or when the user double-clicks the mouse on an item.
- When appropriate, users may even select, or tag, multiple items from
- the list, and each selected item is marked with a special character.
- The tag status of the highlighted option can be toggled by hitting the
- [KEYCAP]. All items can be tagged or untagged at once by pressing [KEY-
- CAP] and [KEYCAP], respectively.
-
- Figure 9.6 shows a typical list window.
-
- Figure 9.6 [SCREEN]
- A Typical List
- Window
-
-
- By default, a list window is stretchable, and if the user changes the
- dimensions, the Toolkit adjusts the displayed columns and rows. Figure
- 9.7 is the same List window as shown in figure 9.6, after the user has
- stretched the window.
-
- Figure 9.7 [SCREEN]
- A Stretched List
- Window
-
-
- The primary Toolkit object for displaying lists in a window is ListOBJ,
- but this is an abstract object which should not be instanciated. Lis-
- tOBJ has a number of descendants designed to display different data
- types in lists. Figure 9.8 illustrates the ListOBJ object hierarchy.
-
- The object hierarchy is similar in principle to the BrowseOBJ hierar-
- chy, with the following main objects:
- ListArrayOBJ Displays a list derived from a string array.
-
- ListLinkOBJ Displays a list derived from any DLLOBJ object or
- descendant.
- ListDirOBJ & These two objects are used for displaying directories
- ListDirSortOBJ in a list window and are discussed in the next chapter.
-
-
-
- Managing Lists 9-19
- --------------------------------------------------------------------------------
-
- Figure 9.8 [PICTURE]
- ListOBJ
- Object Hierarchy
-
-
- Common Methods
-
- LookTOT (in the unit totLook) is used to control the overall look and
- feel of applications developed with the Toolkit. As well as the window-
- related methods, the following two LookTOT methods impact all list
- objects:
- SetListKeys Identifies the keys to globally tag or untag windows,
- and close the list display.
-
- SetListChars Identifies the characters which will highlight the
- active item, as well as the characters which identify
- the tagged and non-tagged items.
- Refer to page 3-12 for further details.
-
-
- Because all the list objects are derived from the ListOBJ, they share a
- set of common methods. The following methods can be used with any of
- the list objects:
-
- SetTopPick(TopPick: longint);
-
- This method identifies the node number of the first node that will be
- displayed in the list window, i.e. the first visible item in the list.
- By default, this is the first item in the list.
-
- SetActivePick(ThePick:longint);
-
- The highlighted item is referred to as the active pick. This procedure
- is used to set which item (or pick) will be highlighted when the list
- is first displayed. The passed parameter represents the number of the
- item in the visible window, with the top left item having a value of
- one. For example, if the top pick (the first visible pick) was item
- number 12 and you wanted to highlight the 15th item, pass a value of
- four. This will instruct the Toolkit to highlight the fourth visible
- pick. The default is the top pick. If an invalid value is used, e.g.
- the value is greater than the number of visible picks, the value is set
- to the top pick.
-
- SetTagging(On:boolean);
-
- This method is used to identify whether the user will be allowed to tag
- and untag items. Pass true to enable item tagging.
-
-
-
-
- 9-20 User's Guide
- --------------------------------------------------------------------------------
-
- SetColWidth(Wid:byte);
-
- This sets the width of each column in characters. If the column width
- is set to zero, the list will be displayed in a single column only, and
- the column width will be as wide as the window. When setting the column
- width, remember to allow for the additional characters used to high-
- light the active pick, and to signify tagged and non-tagged items.
-
- Show;
-
- This method displays the list and immediately returns control to the
- calling procedure, i.e. the Toolkit does not wait for user input. This
- option is useful if you want to display the list on the screen, but not
- activate it.
-
- Go;
-
- This method is the main "do it" method. If the list is not on display,
- it is drawn. The Toolkit then processes user input until an end key is
- pressed.
-
- GetHiString:string;
-
- This function returns a string representing the highlighted item's
- data.
-
- LastKey:word;
-
- LastKey returns the key code of the key the user pressed to end the
- browse session. This is used if you need to know whether the user
- escaped or pressed the done key.
-
- Done;
-
- As always, Done disposes of the Browse window object, and should always
- be called when the object is no longer required.
-
-
- Modifying Window Characteristics
-
- Like BrowseOBJ, the list data is displayed in a window of type Stretch-
- WinOBJ. You can modify any window attribute by directly calling the
- StretchWinOBJ methods. ListOBJ includes a function method, Win, which
- returns a pointer to the StretchWinOBJ instance. To modify the window,
- call the appropriate StretchWinOBJ method using the following syntax:
- [ListOBJInstance].Win^.method
-
- For example, the following statements would change the default window
- size, style and title:
-
- Managing Lists 9-21
- --------------------------------------------------------------------------------
-
- var MyLister: ListLinkOBJ;
-
- begin
- with MyList do
- begin
- Init;
- Win^.SetSize(1,1,50,20,3);
- Win^.SetTitle('My Lister');
- end;
- end.
-
-
- Call Win^ methods to set the list window characteristics, e.g. colors,
- title, remove status, boundaries, etc. Refer to chapter 7: Using Win-
- dows for a thorough discussion of the StretchWinOBJ methods.
-
-
-
- Note: the display colors used in list objects are controlled by
- two different methods. The attributes of the list window perime-
- ter, i.e. box, title, scroll bars and icons, are set using the
- method:
- Win^.SetColors.
- The display attributes for the body of the window, i.e. the items
- in the list, are controlled using the ListOBJ method SetColors,
- which is discussed later in this section.
-
-
-
- Listing Arrays
-
- The ListArrayOBJ object provides an easy way to display a list based on
- the contents of a string array. In addition to the common methods just
- described, there is the following method:
-
- AssignList(var StrArray;Total:longint;
- StrLength:byte;Taggable:boolean);
-
- AssignList identifies the string array that will be displayed. The four
- parameters are the string array, the total number of elements in the
- array, the length of each string in the array, and a boolean to indi-
- cate whether individual items can be tagged. The string length parame-
- ter must reflect the string length of the array when it was declared,
- not the maximum length of any string assigned to the array.
-
- Having assigned the list, all you need to do is call Show, and the
- browse window will pop onto the display. Listed below is the demo pro-
- gram DEMLS1.PAS.
-
-
-
- 9-22 User's Guide
- --------------------------------------------------------------------------------
-
- program DemoList1;
- {demls1}
-
- Uses DOS, CRT,
- totFAST, totLIST;
- Var
- Items : array [1..20] of string[30];
- ListWin: ListArrayObj;
-
- procedure FillArray;
- {}
- begin
- Items[1] := 'One';
- Items[2] := 'Two';
- Items[3] := 'Three';
- Items[4] := 'Four';
- Items[5] := 'Five';
- Items[6] := 'Six';
- Items[7] := 'Seven';
- Items[8] := 'Eight';
- Items[9] := 'Nine';
- Items[10] := 'Ten';
- Items[11] := 'Eleven';
- Items[12] := 'Twelve';
- Items[13] := 'Thirteen';
- Items[14] := 'Fourteen';
- Items[15] := 'Fifteen';
- Items[16] := 'Sixteen';
- Items[17] := 'Seventeen';
- Items[18] := 'Eighteen';
- Items[19] := 'Nineteen';
- Items[20] := 'Twenty';
- end; {FillArray}
- begin
- Screen.Clear(white,'░'); {paint the screen}
- FillArray;
- with ListWin do
- begin
- Init;
- AssignList(Items,20,30,true);
- Go;
- Done;
- end;
- end.
-
-
- This is a very basic list which uses all the default settings. Figure
- 9.9 illustrates the resultant display.
-
-
-
- Managing Lists 9-23
- --------------------------------------------------------------------------------
-
- Figure 9.9 [SCREEN]
- A Basic List
- Window
-
- The following few statements are an extract from the demo file
- DEMLS2.PAS which is very similar to the previous demo, except that the
- list window has been customized somewhat:
- with ListWin do
- begin
- Init;
- AssignList(Items,20,30,true);
- SetColWidth(15);
- Win^.SetTitle(' Pick a number! ');
- Win^.SetSize(24,7,55,18,2);
- Go;
- Done;
- end;
-
-
- By calling the SetColWidth method, the list has been transformed from a
- single list to a multi-column list. Refer back to figure 9.7 to see the
- output generated from DEMLS2.PAS.
- The program DEMLS3.PAS is yet another refinement of the array list
- demo. In this on-disk file, the following statement is added to change
- the list tagging and highlighting characters:
-
- LookTOT^.SetListChars(chr(16),chr(17),chr(14),chr(32));
-
-
- Listing Linked Lists
-
- The ListLinkOBJ is very similar to the ListArrayOBJ. The primary dif-
- ference is that ListLinkOBJ displays the data stored in a DLLOBJ object
- rather than in an array.
- In addition to the common methods described earlier, there are the
- following two methods:
-
-
- AssignList(var LinkList: DLLOBJ);
- This method is passed a DLLOBJ instance, or any instance of an object
- derived from DLLOBJ, e.g. StrDLLOBJ.
-
-
- ListPtr: DLLPtr;
- This method returns a pointer to the DLLOBJ used to create the list.
- You can access the linked list by using the syntax MyList.ListPtr^.me-
- thod.
-
-
-
-
- 9-24 User's Guide
- --------------------------------------------------------------------------------
-
- The demo program DEMLS4.PAS (listed below) shows how well StrDLLOBJ and
- ListLinkOBJ work together. This small demo program creates an StrDLLOBJ
- instance called ItemList. The list is populated by reading the contents
- of an ASCII file. The ListLinkOBJ instance ListWin is then used to
- display the file contents in a window. Figure 9.10 shows the generated
- screen display.
-
- program DemoList4;
- {demls4 - reading a list from a text file}
- Uses DOS, CRT,
- totFAST, totLINK, totLIST;
-
- Var
- ListWin: ListLinkObj;
- ItemList: StrDLLOBJ;
- FileOK: boolean;
- procedure LoadLinkedList;
- {}
- var
- F: text;
- Line:string;
- Result: integer;
- begin
- with ItemList do
- begin
- Init;
- {$I-}
- Assign(F,'demls4.txt');
- Reset(F);
- {$I+}
- FileOK := (IOResult = 0);
- if not FileOK then
- Result := Add('File not found')
- else
- begin
- while not eof(F) do
- begin
- Readln(F,Line);
- Result := Add(Line);
- end;
- close(F);
- end;
- end;
- end; {LoadLinkedList}
-
-
-
- Managing Lists 9-25
- --------------------------------------------------------------------------------
-
- begin
- Screen.Clear(white,'░'); {paint the screen}
- LoadLinkedList;
- with ListWin do
- begin
- Init;
- AssignList(ItemList);
- SetColWidth(15);
- Win^.SetTitle(' Items from file DEMLS4.TXT ');
- Win^.SetSize(20,5,60,20,1);
- if not FileOk then
- SetTagging(false);
- Go;
- Done;
- end;
- ItemList.Done;
- end.
-
-
- Figure 9.10 [SCREEN]
- Display a List
- From a File
-
-
- Determining Which Items are Tagged
-
- Earlier you learned that by calling the method SetTagging(true), you
- could allow the user to tag and untag items. The Toolkit provides the
- method SetStatus to allow you to pre-tag (or, indeed, pre-untag!) items
- before the list is displayed. Similarly, the method GetStatus is used
- to determine the status of any item in the list.
- Before explaining the syntax of these methods, we need to take a little
- peek behind the scenes. The Toolkit stores eight different flags for
- each item in the list. These flags are numbered 0 through 7. Flag 0 is
- used to determine whether the user has tagged the item. Flag 1 indi-
- cates which color to use in dual color mode (discussed later). The
- remaining flags are not used and can be customized for descendant
- objects. Refer to Part 2: Extending the Toolkit for more information.
-
- The syntax of the SetStatus method is as follows:
-
- SetStatus(Pick:longint; BitPos: byte; On:boolean);
-
- To pre-tag an item, call the SetStatus method and pass three parame-
- ters. The first parameter specifies the item to be modified, the second
- parameter is the flag number (and this should be set to 0 (zero) to
- modify the tag flag), and the third boolean parameter should be set to
- True to tag the item, or False to un-tag it.
-
-
-
- 9-26 User's Guide
- --------------------------------------------------------------------------------
-
- After the user has removed the list window, and before the Done method
- is called, the GetStatus method can be called to see which selections
- the user made. The syntax of GetStatus is as follows:
-
-
- GetStatus(Pick:longint; BitPos: byte): boolean;
- This function method returns true if the identified item flag is set
- on. The first parameter identifies the item number, and the second
- parameter should be set to 0 (zero) to get the status of the tag flag.
-
-
- The following demo program, DEMLS5.PAS, shows how to check for all the
- tagged items. The program writes a list of all the tagged items.
- program DemoList5;
- {demls5 - selecting all tagged items}
-
- Uses DOS, CRT,
- totFAST, totLINK, totLIST;
- Var
- ListWin: ListLinkObj;
- ItemList: StrDLLOBJ;
- FileOK: boolean;
- L,Total: longint;
-
- procedure LoadLinkedList;
- {}
- var
- F: text;
- Line:string;
- Result: integer;
- begin
- with ItemList do
- begin
- Init;
- {$I-}
- Assign(F,'demls4.txt');
- Reset(F);
- {$I+}
- FileOK := (IOResult = 0);
- if not FileOK then
- Result := Add('File not found')
- else
- begin
- while not eof(F) do
- begin
- Readln(F,Line);
- Result := Add(Line);
- end;
- close(F);
-
-
- Managing Lists 9-27
- --------------------------------------------------------------------------------
-
- end;
- end;
- end; {LoadLinkedList}
-
- begin
- Screen.Clear(white,'░'); {paint the screen}
- LoadLinkedList;
- with ListWin do
- begin
- Init;
- AssignList(ItemList);
- SetColWidth(15);
- Win^.SetTitle(' Items from file DEMLS4.TXT ');
- Win^.SetSize(20,5,60,20,1);
- if not FileOk then
- SetTagging(false);
- Go;
- Remove;
- Total := ItemList.TotalNodes;
- clrscr;
- for L := 1 to Total do
- if GetStatus(L,0) then
- Writeln('Selected: ',GetString(L,0,0));
- Done;
- end;
- ItemList.Done;
- end.
-
- In a real application, you might call a procedure to process the tagged
- items, rather than simply write a list of all tagged items.
-
- If you are displaying a linked list, you can take advantage of the
- DLLOBJ method DelAllStatus. This method is used to automatically remove
- entries from the list which have one of the status flags set in a
- specified state. The syntax of the DLLOBJ method is as follows:
-
- DelAllStatus(BitPos:byte; On:boolean);
-
- This method removes all entries from the linked list which have the
- specified flag set in the specified state. The first parameter indi-
- cates the flag number, in the range 0 to 7. The second parameter deter-
- mines whether all the flags set to True or False will be deleted. For
- example, to remove all tagged entries in a linked list you would call
- the method DelAllStatus(0,true);.
-
-
-
- 9-28 User's Guide
- --------------------------------------------------------------------------------
-
- Displaying Dual Colored Lists
-
- All list objects are capable of displaying each item in one of two
- color combinations. This facility is put to good effect in the ListDi-
- rOBJ object (discussed in the next chapter), where the files are dis-
- played in one color, and the subdirectories in another. The following
- two methods impact the list display colors:
-
- SetColors(HAttr,NAttr,SAttr: byte);
-
- This method sets the display attributes for the items in the list. The
- first parameter specifies the attribute of the active pick. The other
- two attributes specify the attributes for the normal items. If the
- status (discussed below) of the normal item is set to high, the SAttr
- attribute will be used, otherwise the NAttr will be used.
-
- SetDualColors(On:boolean);
-
- If you want to exploit the dual color capabilities of the list objects,
- pass True. Passing a False will force all the items to display in the
- same color.
-
- The Toolkit has to have some way of identifying the status of each
- item, i.e. in which attribute the color should be displayed. The second
- of the status flags, i.e. flag 1, is designated as the color flag. The
- SetStatus and GetStatus flags are used to control the color feature, as
- follows:
-
-
- SetStatus(Pick:longint; BitPos: byte; On:boolean);
- This method is used to set the status of any item in the list. The
- first parameter identifies the number of the item to be set. The second
- parameter should be set to 1 (one) to indicate that the color status
- flag is being set. The final parameter is a boolean to indicate whether
- status is on (True) or off (False).
-
-
-
- GetStatus(Pick:longint; BitPos: byte): boolean;
- This function method returns true if the identified item flag is set
- on. The first parameter identifies the item number, and the second
- parameter should be set to 1 (one) to determine the status of the dual
- color flag.
-
- An example of the dual color facility is included in the DEMLS7.PAS,
- discussed in the Character Hook section at the end of the chapter.
-
-
-
- Managing Lists 9-29
- --------------------------------------------------------------------------------
-
- Displaying Messages about the Highlighted Pick
-
- The ListOBJ object family supports the optional display of a message at
- the bottom of the list window. This message is normally related to the
- currently highlighted pick. The message might be a long description of
- a terse pick, or some other information about the highlighted pick.
- There are two different ways to instruct the Toolkit to display a mes-
- sage. The no-fuss way is to write a special procedure (called a message
- hook) and instruct the Toolkit to call this procedure every time a pick
- is highlighted. The alternative is to take advantage of an OOP feature
- known as Polymorphism.
-
- Once a message displaying routine has been implemented, it can be dis-
- abled and enabled using the following method:
-
- SetMsgState(On:boolean);
-
- Pass True to activate the message display, or False to deactivate it.
-
-
- Using a Message Hook
- A message hook is an external function which is called every time a new
- pick is highlighted. To utilize the message hook facility, all you have
- to do is create a function following some specific rules, and then call
- the method SetMsgHook to instruct the Toolkit to use your function.
-
- For a function to be eligible as a message hook it must adhere to the
- following 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 function statement.
-
- Rule 2 The function must be declared with one passed parameter of
- type longint. This parameter indicates the highlighted item
- number.
- Rule 3 The function must return a value of type string. This return
- value is the actual text to be displayed in the message area
- of the window display.
-
- Rule 4 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:
-
-
-
- 9-30 User's Guide
- --------------------------------------------------------------------------------
-
- {$F+}
- function MyMessageHook(HiPick:longint): string;
- begin
- ...{function statements}
- MyMessageHook := 'something!';
- end;
- {$F-}
-
-
- The following method SetMsgHook is then called to instruct the Toolkit
- to call your function every time a new item is highlighted:
-
- SetMsgHook(Func:ListMsgFunc);
-
- This method is passed the function name of a function declared using
- the rules outlined above, e.g. SetMsgHook(MyMessageHook);
-
-
- The demo program DEMLS6 (listed below) implements a message hook. In
- this case the message is simply a phrase stating which topic is high-
- lighted. I hope your applications are a little more useful! Figure 9.11
- illustrates the resultant output.
-
-
- program DemoList6;
- {demls6 - displaying a list message}
- Uses DOS, CRT,
- totFAST, totLINK, totLIST, totSTR, totMSG;
-
- Var
- ListWin: ListLinkObj;
- ItemList: StrDLLOBJ;
- FileOK: boolean;
- {$F+}
- function MsgHook(HiPick:longint):string;
- {}
- begin
- MsgHook := 'The Hi Pick is '+IntToStr(HiPick);
- end; {MsgHook}
- {$F-}
-
- procedure LoadLinkedList;
- {}
- var
- F: text;
- Line:string;
- Result: integer;
- begin
- with ItemList do
-
-
-
- Managing Lists 9-31
- --------------------------------------------------------------------------------
-
- begin
- Init;
- {$I-}
- Assign(F,'demls4.txt');
- Reset(F);
- {$I+}
- FileOK := (IOResult = 0);
- if not FileOK then
- Result := Add('File not found')
- else
- begin
- while not eof(F) do
- begin
- Readln(F,Line);
- Result := Add(Line);
- end;
- close(F);
- end;
- end;
- end; {LoadLinkedList}
-
- begin
- Screen.Clear(white,'░'); {paint the screen}
- LoadLinkedList;
- with ListWin do
- begin
- Init;
- AssignList(ItemList);
- SetColWidth(15);
- SetMsgHook(MsgHook);
- Win^.SetTitle(' A List With Messages ');
- Win^.SetSize(20,5,60,20,2);
- Win^.SetMinSize(20,7);
- if not FileOk then
- SetTagging(false);
- Go;
- Done;
- end;
- ItemList.Done;
- end.
-
- Figure 9.11 [SCREEN]
- A List with a Mes-
- sage
-
-
-
- 9-32 User's Guide
- --------------------------------------------------------------------------------
-
- Creating a Descendant Object
-
- By design, most of the documentation on customizing the Toolkit with
- object oriented techniques is to be found in Part 2: Extending the
- Toolkit. However, as an appetizer, this section explains how to take
- advantage of OOP to implement your own message method, without needing
- to pass a procedure as a parameter. The message hook procedure
- described in the previous section is perfectly adequate and acceptable,
- but it isn't OOP! If you want to learn a few OOP tidbits, read on,
- otherwise be content with the message hook and skip to the next sec-
- tion.
- Usually, to customize a Toolkit object to better meet your needs, you
- create a descendant object. For illustration, we will modify the List-
- LinkOBJ object. Whenever you create a descendant object, you should
- study the parent object's methods and decide which ones to replace or
- modify.
-
- A descendant object contains all the data and methods of its parent. As
- a bare minimum, you should always define new Init and Done methods, and
- then replace any other methods that you wish to improve or modify. (You
- can also add new methods, but we'll leave that to Part 2!).
- The ListLinkOBJ method includes the following virtual method:
-
- function MessageTask(HiPick:longint):string; VIRTUAL;
- This method is called every time a new topic is highlighted, and it
- returns the text to be displayed. Since this method is declared VIR-
- TUAL, we can replace the method in a descendant object, and other
- methods will use the newly defined version of MessageTask.
-
- The objective is to create a new object descendant from ListLinkOBJ,
- and replace the MessageTask method with a new routine. The syntax for
- declaring the new object type is as follows:
- Type
- NewListLinkOBJ = object (ListLinkOBJ)
- constructor Init;
- function MessageTask(HiPick:longint):string; VIRTUAL;
- destructor Done; VIRTUAL;
- end; {NewListLinkOBJ}
-
-
- The descendant object will be called NewListLinkOBJ. Notice that the
- special constructor and destructor procedures Init and Done are
- declared, as well as the new MessageTask function. The constructor and
- destructor methods are required when an object includes virtual meth-
- ods, as it instructs Turbo Pascal to manage procedure calls differ-
- ently. The keywords constructor and destructor are simply used as
- replacements for procedure. The de facto OOP standard is to name the
- constructor INIT and the destructor DONE.
-
-
-
- Managing Lists 9-33
- --------------------------------------------------------------------------------
-
- If the ancestor method is declared VIRTUAL, the descendant method must
- also be declared virtual. Furthermore, when you substitute virtual
- methods in a descendant object, the declaration of the method must be
- exactly the same as the ancestor. For this reason, MessageTask and Done
- are both declared virtual, with the same parameters as ListLinkOBJ.
-
- Having declared a new type NewListLinkOBJ, you must write the actual
- object methods in the body of your program or unit. In this case, Turbo
- Pascal expects the three object methods Init, MessageTask and Done.
- Listed below is an example of how these methods might be written.
- constructor NewListLinkOBJ.Init;
- {}
- begin
- ListLinkOBJ.Init;
- vMsgActive := true;
- end; {NewListLinkOBJ.Init}
-
- function NewListLinkOBJ.MessageTask(HiPick:longint):string;
- {}
- begin
- MessageTask := 'The Hi Pick is '+IntToStr(HiPick);
- end; {NewListLinkOBJ.MessageTask}
-
- destructor NewListLinkOBJ.Done;
- {}
- begin
- ListLinkOBJ.Done;
- end; {NewListLinkOBJ.Done}
-
- Notice that each method starts with the object name followed by a
- period followed by the method identifier. Whenever you declare an Init
- or Done method, you should always call the ancestors Init or Done --
- very often, this ancestor method performs important data initialization
- tasks, and let's not forget that a descendant object assumes all the
- data of its ancestor.
- In the new Init method, the variable vMsgActive is set to true. (All
- object variables in the Toolkit commence with the letter "v".) This
- variable indicates that the object should always call the message dis-
- play method when the active topic is changed. In the LinkListOBJ, this
- variable defaults to false, and is changed to true when the method
- SetMsgHook is called. The main purpose of this descendant object is to
- replace the old MessageTask method with a new one. In the new method,
- the string value returned is just the statement identifying the high-
- lighted topic.
-
- That's it. Any instance of NewListLinkOBJ will automatically display
- the string returned by the new MessageTask method. Listed below is the
- example DEMLS7.PAS which shows the example in its entirety.
-
-
-
- 9-34 User's Guide
- --------------------------------------------------------------------------------
-
- program DemoList7;
- {demls7 - creating a descendant ListObject}
-
- Uses DOS, CRT,
- totFAST, totLINK, totLIST, totSTR, totMSG;
- Type
- NewListLinkOBJ = object (ListLinkOBJ)
- {Methods...}
- Constructor Init;
- function MessageTask(HiPick:longint):string; VIRTUAL;
- destructor Done; VIRTUAL;
- end; {NewListLinkOBJ}
-
- Var
- ListWin: NewListLinkObj;
- ItemList: StrDLLOBJ;
- FileOK: boolean;
-
- {+++++new object methods+++++}
- constructor NewListLinkOBJ.Init;
- {}
- begin
- ListLinkOBJ.Init;
- vMsgActive := true;
- end; {NewListLinkOBJ.Init}
- function NewListLinkOBJ.MessageTask(HiPick:longint):string;
- {}
- begin
- MessageTask := 'The Hi Pick is '+IntToStr(HiPick);
- end; {NewListLinkOBJ.MessageTask}
-
- destructor NewListLinkOBJ.Done;
- {}
- begin
- ListLinkOBJ.Done;
- end; {NewListLinkOBJ.Done}
- {+++++end of new object methods+++++}
- procedure LoadLinkedList;
- {}
- var
- F: text;
- Line:string;
- Result: integer;
- begin
- with ItemList do
- begin
- Init;
- {$I-}
-
-
-
- Managing Lists 9-35
- --------------------------------------------------------------------------------
-
- Assign(F,'demls4.txt');
- Reset(F);
- {$I+}
- FileOK := (IOResult = 0);
- if not FileOK then
- Result := Add('File not found')
- else
- begin
- while not eof(F) do
- begin
- Readln(F,Line);
- Result := Add(Line);
- end;
- close(F);
- end;
- end;
- end; {LoadLinkedList}
-
- begin
- Screen.Clear(white,'░'); {paint the screen}
- LoadLinkedList;
- with ListWin do
- begin
- Init;
- AssignList(ItemList);
- SetColWidth(15);
- Win^.SetTitle(' A List With Messages ');
- Win^.SetSize(20,5,60,20,2);
- Win^.SetMinSize(20,7);
- if not FileOk then
- SetTagging(false);
- Go;
- Done;
- end;
- ItemList.Done;
- end.
-
- This example works just like DEMLS6.PAS, but a descendant object was
- created rather than passing a procedure. I know what you are thinking
- "It's a damn sight easier to pass a procedure!". That's true, and that
- is precisely why the Toolkit includes the procedure passing alterna-
- tive. By design, this first taste of OOP was a little simple. If your
- curiosity has been piqued, review the source code in the unit
- TOTLIST.PAS. You will see how the base object ListOBJ does all the
- work, and how the descendant objects ListArrayOBJ and ListLinkOBJ make
- the objects work for string arrays and linked lists. The totDIR unit
- discussed in the next chapter extends the ListLinkOBJ and customizes it
- specifically for displaying files and directories.
-
-
-
- 9-36 User's Guide
- --------------------------------------------------------------------------------
-
- Character Hooks
-
- The ListOBJ object family provides ways to intercept every key pressed
- by the user. This allows you to implement your own special hotkeys.
- Like the Message facility described earlier, there are two different
- ways to instruct the Toolkit to call a procedure every time a key is
- pressed -- by passing a procedure, or by creating a descendant object.
-
-
- Using a Character Hook
-
- A character hook is an external procedure which is called every time a
- key or mouse button is pressed. To utilize the character hook facility,
- all you have to do is create a function following some specific rules,
- and then call the method SetCharHook to instruct the Toolkit to use
- your function.
- For a function to be eligible as a character hook it must adhere to the
- following 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 function statement.
- Rule 2 The function must be declared with four (count 'em) passed
- parameters. Parameter one must be a variable parameter of
- type word. This parameter indicates which key the user just
- pressed, and you may change the value of this parameter to
- substitute a different key. The second and third parameters
- must be variable parameters of type byte, and they represent
- the X and Y coordinates of the mouse at the time the key was
- pressed. The fourth parameter is non-variable, and must be
- of type longint. This parameter indicates the highlighted
- item number.
-
- Rule 3 The function must return a value of type tListAction. This
- is an enumerated type which indicates to the Toolkit how to
- proceed. The members of the enumerated type are: Finish,
- Refresh and None. If you want the list window to terminate,
- return Finish. If you have changed, inserted, or deleted any
- items in the visible list, return Refresh. The Toolkit will
- then re-display the entire window contents. Normally, how-
- ever, you will just return None.
- Rule 4 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:
-
-
-
- Managing Lists 9-37
- --------------------------------------------------------------------------------
-
- {$F+}
- function MyCharHook(var K:word;
- var X,Y: byte;
- HiPick:longint): tListAction;
- begin
- ...{function statements}
- MyCharHook := None;
- end;
- {$F-}
-
-
- The following method SetCharHook is then called to instruct the Toolkit
- to call your function every time a key is pressed:
-
- SetCharHook(Func:ListCharFunc);
-
- This method is passed the function name of a function declared using
- the rules outlined above, e.g. SetCharHook(MyCharHook);.
-
- The demo program DEMLS8.PAS, listed below, implements both a message
- hook and a character hook. The character hook is checking for two spe-
- cial keys. If [KEYCAP] is pressed, a simple help screen is displayed,
- and if [KEYCAP] is pressed, the topic is changed to the alternative
- color.
-
- program DemoList8;
- {demls8 - using Message and Character hooks}
- Uses DOS, CRT,
- totFAST, totLINK, totLIST, totSTR, totMSG;
-
- Var
- ListWin: ListLinkObj;
- ItemList: StrDLLOBJ;
- FileOK: boolean;
- {$F+}
- function HelpHook(var K:word; var X,Y: byte; HiPick:longint): tListAc-
- tion;
- {}
- var MsgWin: MessageOBJ;
- begin
- HelpHook := None;
- if K = 315 then
- begin
- with MsgWin do
- begin
- Init(6,'Kinda Help');
- AddLine('');
- AddLine('In a real application, this would');
-
-
-
- 9-38 User's Guide
- --------------------------------------------------------------------------------
-
- AddLine('be a help screen, and it would give');
- AddLine('help related to item '+IntToStr(HiPick)+'!');
- AddLine('');
- Show;
- Done;
- end;
- K := 0;
- end
- else if K = 316 then {F2 so swap colors}
- begin
- ListWin.SetStatus(HiPick,1,not ListWin.GetStatus(HiPick,1));
- K := 336; {emulate down cursor}
- end;
- end; {HelpHook}
-
- function MsgHook(HiPick:longint):string;
- {}
- begin
- MsgHook := 'The Hi Pick is '+IntToStr(HiPick);
- end; {MsgHook}
- {$F-}
- procedure LoadLinkedList;
- {}
- var
- F: text;
- Line:string;
- Result: integer;
- begin
- with ItemList do
- begin
- Init;
- {$I-}
- Assign(F,'demls4.txt');
- Reset(F);
- {$I+}
- FileOK := (IOResult = 0);
- if not FileOK then
- Result := Add('File not found')
- else
- begin
- while not eof(F) do
- begin
- Readln(F,Line);
- Result := Add(Line);
- end;
- close(F);
-
-
-
- Managing Lists 9-39
- --------------------------------------------------------------------------------
-
- end;
- end;
- end; {LoadLinkedList}
-
- begin
- Screen.Clear(white,'░'); {paint the screen}
- Screen.WriteCenter(25,white,' F1 Help F2 Toggle Color! [Space]
- Toggle Tag ');
- LoadLinkedList;
- with ListWin do
- begin
- Init;
- AssignList(ItemList);
- SetColWidth(15);
- SetCharHook(HelpHook);
- SetMsgHook(MsgHook);
- SetDualColors(true);
- Win^.SetTitle(' A Multi-Colored List ');
- Win^.SetSize(20,5,60,20,2);
- Win^.SetMinSize(20,7);
- if not FileOk then
- SetTagging(false);
- Go;
- Done;
- end;
- ItemList.Done;
- end.
-
- Figure 9.12 [SCREEN]
- Using a
- Character Hook
-
- Creating a Descendant Object
-
- I think we've had enough OOP for one chapter. Suffice it to say that
- just like the message hook, the character hook can be implemented by
- creating a descendant object and replacing a ListLinkOBJ method called
- CharTask.
- The demo file DEMLS9.PAS (listed below) shows how to replace both the
- character hook and the message hook by creating a new descendant
- object.
-
- program DemoListNine;
- {demls9 - extending the LinkListOBJ object}
- Uses DOS, CRT,
- totFAST, totLINK, totLIST, totSTR, totMSG;
-
-
-
- 9-40 User's Guide
- --------------------------------------------------------------------------------
-
- Type
- NewListLinkOBJ = object (ListLinkOBJ)
- {Methods...}
- Constructor Init;
- function MessageTask(HiPick:longint):string; VIRTUAL;
- function CharTask(var K:word; var X,Y: byte;
- HiPick:longint): tListAction; VIRTUAL;
- destructor Done; VIRTUAL;
- end; {NewListLinkOBJ}
-
- Var
- ListWin: NewListLinkObj;
- ItemList: StrDLLOBJ;
- FileOK: boolean;
- {+++++new object methods+++++}
- constructor NewListLinkOBJ.Init;
- {}
- begin
- ListLinkOBJ.Init;
- vMsgActive := true;
- end; {NewListLinkOBJ.Init}
-
- function NewListLinkOBJ.MessageTask(HiPick:longint):string;
- {}
- begin
- MessageTask := 'The Hi Pick is '+IntToStr(HiPick);
- end; {NewListLinkOBJ.MessageTask}
-
- function NewListLinkOBJ.CharTask(var K:word; var X,Y: byte;
- HiPick:longint): tListAction;
- {}
- var MsgWin: MessageOBJ;
- begin
- CharTask := none;
- if K = 315 then
- begin
- with MsgWin do
- begin
- Init(6,'Kinda Help');
- AddLine('');
- AddLine('In a real application, this would');
- AddLine('be a help screen, and it would give');
- AddLine('help related to item '+IntToStr(HiPick)+'!');
- AddLine('');
- Show;
- Done;
- end;
- K := 0;
-
-
-
- Managing Lists 9-41
- --------------------------------------------------------------------------------
-
- end
- else if K = 316 then {F2 so swap colors}
- begin
- ListWin.SetStatus(HiPick,1,not ListWin.GetStatus(HiPick,1));
- K := 336; {emulate down cursor}
- end;
- end; {NewListLinkOBJ.CharTask}
-
- destructor NewListLinkOBJ.Done;
- {}
- begin
- ListLinkOBJ.Done;
- end; {NewListLinkOBJ.Done}
- {+++++end of new object methods+++++}
-
- procedure LoadLinkedList;
- {}
- var
- F: text;
- Line:string;
- Result: integer;
- begin
- with ItemList do
- begin
- Init;
- {$I-}
- Assign(F,'demls4.txt');
- Reset(F);
- {$I+}
- FileOK := (IOResult = 0);
- if not FileOK then
- Result := Add('File not found')
- else
- begin
- while not eof(F) do
- begin
- Readln(F,Line);
- Result := Add(Line);
- end;
- close(F);
- end;
- end;
- end; {LoadLinkedList}
- begin
- Screen.Clear(white,'░'); {paint the screen}
- Screen.WriteCenter(25,white,' F1 Help F2 Toggle Color! [Space]
- Toggle Tag ');
- LoadLinkedList;
-
-
-
- 9-42 User's Guide
- --------------------------------------------------------------------------------
-
- with ListWin do
- begin
- Init;
- AssignList(ItemList);
- SetColWidth(15);
- SetDualColors(true);
- Win^.SetTitle(' A Multi-Colored List ');
- Win^.SetSize(20,5,60,20,2);
- Win^.SetMinSize(20,7);
- if not FileOk then
- SetTagging(false);
- Go;
- Done;
- end;
- ItemList.Done;
- end.