From: kurisuto@BACH.UDEL.EDU ("Sean J. Crist") Subject: More free code: Simplifying the List Manager Date: 18 Sep 92 03:33:13 GMT The following code, once again, is nothing particularly glamorous; it simply makes it easier to use the List Manager to create and manage lists of strings. One of the most common uses for the List Manager is scrollable, one-dimensional, fixed-size lists of strings (as in SFGetFile, SFPutFile). The List Manager is good for creating all kinds of lists (such as lists of icons), but a lot of this functionality is a hassle for programmers who only need a simple list of strings. The code below allows you create and dispose of lists of strings. It allows you to add, rename, and remove elements in the list, and handles mouse clicks and update events. It also keeps the lists in alphabetical order. I remember having a lot of trouble learning how to call the List Manager properly; I hope that this code helps somebody else. How to use these routines: Every one of these routines takes a ListHandle as one of its parameters. It is OK to have several lists going at one time; just make sure you pass the right ListHandle when you call one of these routines. CreateList: Call this routine to create a new list of strings. Pass it an empty ListHandle, as well as the pointer to the window to contain the list, and the rectangle in which the list should be enclosed. The list can take up an entire window, or only part of it, as you prefer. The scroll bar will be drawn inside the rectangle you specify, so you don't need to leave extra room for it. Initially, the list will contain no strings. UpdateList: Call this in response to an Update event to redraw the portions of the list which need it. This routine assumes that you have already called BeginUpdate for the window containing the list, and that you have not yet called EndUpdate. DoClick: Call this function in response to a MouseDown event inside your list's rectangle. DoClick usually returns FALSE, but it returns TRUE if this click is the second click of a double-click (i.e., TRUE means the user double-clicked an item). TurnOffSelection: This routine unhilights the currently hilighted cell, if any. ListSelection: This returns the string of the currently selected cell. If no cell is currently selected, the empty string is returned. AddCell: Call this routine to add a new cell to the list. The string you pass to this routine is automatically alphabetized within the list. Bug alert: the first element or two of the list are sometimes not in alphabetical order. If you take the time to work out this kink, please let me know what you did (I could figure this out, but I just haven't taken the time to). This bug is cosmetic and does not crash the program. RenameCell: Changes the string in a cell to another string, and realphabetizes the list, if necessary. DeleteCell: Removes the cell with the given string from the list. DisposList: Call this routine when you are completely finished with a list and want to deallocate its memory. Credit: The routines CreateList, UpdateList, DoClick, and DisposList are loosely based on some code I received from somebody of comp.sys.mac.programmer around two years ago. I've rewritten these routines, but credit is due to this contributor, whose name I cannot remember. I have successfully called these routines for lists in regular windows as well as in modal dialogs. Please send bug reports, praise, money, etc. to kurisuto@bach.udel.edu. unit StringListInterface; interface {Create a new list with no elements.} procedure CreateList (var TheListHandle: ListHandle; TheWindow: WindowPtr; TheRect: Rect); {Update the art of a list.} procedure UpdateList (TheListHandle: ListHandle); {Handle a click in the list rectangle. If it was a double-click, we will return TRUE.} function DoClick (TheListHandle: ListHandle; TheWhere: Point): Boolean; {Turn off any hilited item.} procedure TurnOffSelection (TheListHandle: ListHandle); {Return the string currently selected; empty string if no selection.} function ListSelection (TheListHandle: ListHandle): string; {Add a new cell containing the string parameter to the end of the list} procedure AddCell (TheListHandle: ListHandle; NewString: str255); {Change the name of an existing cell} procedure RenameCell (TheListHandle: ListHandle; OldString, NewString: Str255); {Remove the cell with the given name from the list.} procedure DeleteCell (TheListHandle: ListHandle; TheString: string); {Get rid of the list when we're done with it, cleaning up all the memory.} procedure DisposList (TheListHandle: ListHandle); implementation procedure CreateList; const StandardList = 0; var ViewRect: Rect; DataBounds: Rect; CellSize: Point; TempInteger: Integer; {Just to do a little math} begin {Inset the box to make room for the scroll bar. Also inset it so we've got room for a border.} ViewRect := TheRect; InsetRect(ViewRect, 1, 1); ViewRect.Right := ViewRect.Right - 15; {Set the cell size to the size of the cell} CellSize.v := TheWindow^.txSize + 3; if CellSize.v = 3 then {If it hasn't been set, then make it 12 point.} begin TextSize(12); CellSize.v := 15; end; CellSize.h := ViewRect.Right - ViewRect.Left; {Now adjust the ViewRect to avoid cutting off the last visible cell} TempInteger := (ViewRect.Bottom - ViewRect.Top) div CellSize.v; ViewRect.Bottom := ViewRect.Top + (TempInteger * CellSize.v); {Create the new list.} SetRect(DataBounds, 0, 0, 1, 0); TheListHandle := LNew(ViewRect, DataBounds, CellSize, StandardList, TheWindow, FALSE, FALSE, FALSE, TRUE); UpdateList(TheListHandle); end; {Update the display of a list.} procedure UpdateList; var ViewRect: Rect; ListUpdateRgn: RgnHandle; begin SetPort(TheListHandle^^.Port); {Get the List manager to update the list.} ViewRect := TheListHandle^^.rView; LDoDraw(true, TheListHandle); ListUpdateRgn := NewRgn; RectRgn(ListUpdateRgn, ViewRect); LUpdate(ListUpdateRgn, TheListHandle); {Draw the border} InsetRect(ViewRect, -1, -1); FrameRect(ViewRect); {Clean up after ourselves} DisposeRgn(ListUpdateRgn); end; {Handle a click in the list rectangle. If it was a double-click, we will return TRUE.} function DoClick; begin SetPort(TheListHandle^^.Port); LDoDraw(TRUE, TheListHandle); DoClick := LClick(TheWhere, 0, TheListHandle); end; {Turn off any hilited item.} procedure TurnOffSelection; var ResultPoint: Point; begin SetPt(ResultPoint, 0, 0); if LGetSelect(TRUE, ResultPoint, TheListHandle) then LSetSelect(FALSE, ResultPoint, TheListHandle); end; {Return the string currently selected; empty string if no selection.} function ListSelection; var ResultPoint: Point; ResultString: Str255; StringPointer: Ptr; StringLength: Integer; begin SetPt(ResultPoint, 0, 0); if LGetSelect(TRUE, ResultPoint, TheListHandle) then {If there is a cell selected, then get the string value of that string. There ought to be an} {easier way to do this than mucking around in the memory like this. >:-( } begin {If there is a cell selected, then return the string of the cell.} StringPointer := Ptr(Ord(@ResultString) + 1); StringLength := 255; {This is the maximum amount of data we are allowed to move.} LGetCell(StringPointer, StringLength, ResultPoint, TheListHandle); StringPointer := Ptr(Ord(@ResultString)); StringPointer^ := StringLength; ListSelection := ResultString; end else {Otherwise, return the empty string to show that nothing is selected.} ListSelection := ''; end; {Add a new cell containing the string parameter to the end of the list} procedure AddCell; var Counter: Integer; CellPoint: Point; OldString: Str255; CompResult: Integer; StringLength: Integer; StringPointer: Ptr; done: Boolean; begin {Step 1: Circle through the loop and figure out where we should insert the new} {cell. We do this to put the list in alphabetical order, and to keep it that way as} {new items are added.} CellPoint.h := 0; CellPoint.v := 0; Done := false; while not done do begin if LNextCell(TRUE, TRUE, CellPoint, TheListHandle) then begin StringPointer := Ptr(Ord(@OldString) + 1); StringLength := 255; {This is the maximum amount of data we are allowed to move.} LGetCell(StringPointer, StringLength, CellPoint, TheListHandle); StringPointer := Ptr(Ord(@OldString)); StringPointer^ := StringLength; {Now, compare the new string with the contents of the cell and decide whether the new string} {should come before or after this cell.} CompResult := RelString(NewString, OldString, false, true); case CompResult of sortsBefore, sortsEqual: done := true; SortsAfter: ; end; end else {There are no more rows, so that's all.} begin done := true; end; end; {Add the new row at the top of the list.} CellPoint.v := LAddRow(1, CellPoint.v, TheListHandle); {Put the string into the cell. Once again, there ought to be an easier way to do this.} LSetCell(Pointer(Ord(@NewString) + 1), Length(NewString), CellPoint, TheListHandle); end; procedure RenameCell; var CellPoint: Point; DataPtr: Ptr; DataLen: Integer; begin SetPt(CellPoint, 0, 0); DataPtr := Pointer(Ord(@OldString) + 1); dataLen := Length(OldString); if LSearch(dataPtr, dataLen, nil, CellPoint, TheListHandle) then begin DataPtr := Pointer(Ord(@NewString) + 1); dataLen := Length(NewString); LSetCell(DataPtr, dataLen, CellPoint, TheListHandle); end else begin {The programmer asked us to rename a cell which doesn't exist. We'll just beep angrily} {three times. It's the programmer's responsibility to see that the cell in question actually} {does exist before calling this routine.} Sysbeep(1); Sysbeep(1); Sysbeep(1); end; end; {Remove the cell with the given name from the list.} procedure DeleteCell; var CellPoint: Point; DataPtr: Ptr; DataLen: Integer; begin SetPt(CellPoint, 0, 0); DataPtr := Pointer(Ord(@TheString) + 1); dataLen := Length(TheString); if LSearch(dataPtr, dataLen, nil, CellPoint, TheListHandle) then begin LDelRow(1, CellPoint.v, TheListHandle); end else begin {The programmer asked us to delete a cell which doesn't exist. We'll just beep angrily} {three times. It's the programmer's responsibility to see that the cell in question actually} {does exist before calling this routine.} Sysbeep(1); Sysbeep(1); Sysbeep(1); end; end; {Get rid of the list when we're done with it, cleaning up all the memory.} procedure DisposList; begin LDispose(TheListHandle); end; end.