You Too Can Have A Dynamic Memory: Flexible String Gadget Requester Using Dynamic Memory Allocation

by Randy Finch

I have been working on a C program that requires several requesters containing only string gadgets and two boolean gadgets (OK and CANCEL). The problem I encountered was that each requester needed a different number of string gadgets. Not wanting to write a different function for each requester, I decided to write one function capable of generating every requester I needed. To accomplish this task, dynamic memory allocation was used.

Dynamic Memory Allocation on the Amiga

After studying the various methods of allocating memory on the Amiga, I decided that using the AllocRemember function provided by the Intuition library would be the simplest method. This function creates a linked list of memory blocks using a Remember structure (defined in intuition.h). A Remember structure has the following form:

	struct Remember {
		struct Remember *NextRemember;
		ULONG  RememberSize;
		UBYTE *Memory;
	}

This structure contains a pointer to another Remember structure, an unsigned long integer containing the size of the memory block, and a pointer to the memory block allocated. The NextRemember pointer makes the linked list of memory blocks possible.

In order to create a linked list, a pointer to a Remember structure must be declared and initialized to NULL as follows:

	struct Remember *MyMemBlocks = NULL;

Suppose that two blocks of memory, one 100 bytes long and another 200 bytes long, are needed by the program. The AllocRemember function is called as follows:

	mem_pointer1 = AllocRemember(&MyMemBlocks,100,MEMF_CLEAR);

The first argument is the address of the Remember structure pointer. It is important to keep in mind that this IS NOT a pointer to a Remember structure, but a pointer to that pointer. This is illustrated in Figure 1. The second argument is the amount of memory to allocate, in this case 100 bytes. The third argument contains flags (defined in memory.h); MEMF_CLEAR means to fill the memory block with zeroes after allocation. Other flags include:

	MEMF_FAST		Allocate fast (external) memory
	MEMF_CHIP		Allocate chip (internal 512K) memory
	MEMF_PUBLIC		Allocate memory for different tasks or interrupt code
				   such as task control blocks, messages, ports, etc.

If neither of the above flags is set then MEMF_FAST is assumed first. If no fast memory is available, chip memory is used.

The AllocRemember function returns a pointer to the allocated block of memory if it is successful or NULL if it fails. AllocRemember actually performs two memory allocations, one for the Remember structure and one for the memory block requested. It also fills in the Remember structure and sets the variable MyMemBlocks to point to the structure. This is represented graphically in Figures 2a and 2b.

In order to allocate the 200-byte block of memory, the AllocRemember function is called again in a similar fashion to before.

	mem_pointer2 = AllocRemember(&MyMemBlocks,200,MEMF_CLEAR);

Here the function creates another Remember structure and sets the NextRemember element of the previous Remember structure to point to this new structure. Also, a 200-byte block of memory is allocated (see Figure 2c).

Because of the manner in which the AllocRemember function is called, it is very easy to allocate multiple blocks of memory of equal size. For instance, to create 10 blocks of memory that will each contain an IntuiText structure, the method shown in Listing 1 can be used.

After this code is executed, IntuiTextBlocks will point to the first of 10 Remember structures that are linked together through the NextRemember pointers. Each memory block can be accessed through the Memory element of its corresponding Remember structure. These pointers can be cast to type "pointer to an IntuiText structure" and then the memory blocks can be filled in with data as shown in Listing 2.

When the memory blocks are no longer needed, the FreeRemember function can be called.

	FreeRemember(&IntuiTextBlocks, TRUE);

This function will deallocate all the memory blocks in the list as well as the memory used by the Remember structures. If the second argument is FALSE, all the Remember structures will be erased but the memory blocks will remain intact.

The method just described was used to dynamically allocate memory for various structures and buffers in a function which can create a requester that contains a variable number of string gadgets. This function is entitled StringsRequest and is part of the program entitled StrReq.c shown in Listing 3.

The StringsRequest Function

Six separate linked lists of memory blocks are used in the StringsRequest function. Four of these linked lists each contain NumEntrys Remember structures, where NumEntrys is the number of string gadgets required in the requester. These Remember structures point to memory blocks containing all the Gadget structures and their associated IntuiText and StringInfo structures as well as buffer memory for the gadget strings. The fifth and sixth linked lists actually only contain one Remember structure each. One points to a memory block containing a Requester structure while the other points to an undo buffer.

The IntuiText structures supply information about text related to each gadget. This text will appear to the left of each string gadget and can be used to identify the type of information the user should supply in the gadget. The StringInfo structures supply information about the strings that actually appear in the gadgets. One piece of information in this structure is a pointer to a buffer where the actual text is stored. There is a separate buffer for each gadget. The structure also contains a pointer to an undo buffer. This buffer is filled in with a copy of a string gadget buffer when the gadget is first selected. After changes are made to the string in the gadget, the user can revert back to the original contents of the gadget by pressing Right AMIGA-Q. One undo buffer is sufficient for all the gadgets because only one gadget can be active at a time. The Requester structure ties all of the above information together.

How to Use the StringsRequest Function

The StringsRequest function is of type BOOL; it will return TRUE if the requester is opened and closed successfully, or it will return FALSE otherwise. The eight parameters passed to the function are described in Table I.

Intuition will not allow a string gadget to be wider than the maximum length of the string it contains. Therefore if an element of the MaxLenDefText array is smaller than the corresponding element in the BoxWidth array, the gadget width will be equal to MaxLenDefText rather than BoxWidth. The StringsRequest function takes this into account when determining the size of the requester.

If an element of the TypeDefText array is equal to LONGINT, Intuition will only allow long integers to be entered into the string gadget. This is useful if the information needed is a number. The initial value in the corresponding DefText array element must still be a string; therefore, it is necessary to convert the long integer to an ASCII string before calling StringsRequest. Once the function returns control to the calling routine, the ASCII string must be converted back to an integer.

The StringsRequest function uses the information supplied by its parameters to determine the necessary width and height of the requester so it can contain the string gadgets and associated text. It first allocates the necessary amount of memory, fills in this memory with all the necessary data structures, and then displays the requester. Figure 3 shows a sample requester containing five string gadgets. Because the gadgets are displayed in column format, the requester is limited to 10 string gadgets in the requester on a full screen window in non-interlaced mode or 20 gadgets in interlaced mode.

The requester also contains two boolean gadgets, one displaying OK and another displaying CANCEL. After changes are made to the text in the string gadgets, the OK gadget can be selected to assign the new entries to the strings pointed to by the DefText array. If CANCEL is selected, the text that was originally in the gadgets when the requester was displayed remains intact. After choosing OK or CANCEL, the requester will disappear, the memory allocated to the various data structures will be returned to the free memory pool, and control will be returned to the calling program.

The short program entitled TestSR shown in Listing 4 can be compiled and linked to StrReq.c. It demonstrates how the StringsRequest function works. The program prompts the user on the screen for information about the string gadgets to be displayed in the requester. Alternately, a text file containing all of this information can be created and the program can be executed with the input redirected to this file. TestSR will read the information, open a window, and display the requester. After changes are made and either the OK or CANCEL gadget is chosen, the window will close, and the current text from each gadget will be displayed.

I hope this function and the programming techniques used therein are useful to you.


Back to list of articles
Back to Randy Finch's Home Page
Last modified on April 1, 1996
Randy Finch at webmaster@rcfinch.com