home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
500-599
/
ff502.lzh
/
CELLS
/
CELLSSource.lzh
/
cList.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-04-20
|
22KB
|
791 lines
/*
* CELLS An Implementation of the WireWorld cellular automata
* as described in Scientific American, Jan 1990.
*
* Copyright 1990 by Davide P. Cervone.
* You may use this code, provided this copyright notice is kept intact.
* See the CELLS.HELP file for complete information on distribution conditions.
*/
/*
* File: cList.c Handles name lists for requesters
* (still pretty kludgy, need work)
*/
#include "Cells.h"
#include "cRequest.h"
#include "cBoxes.h"
#define SLIDERINFO ((struct PropInfo *)(theList->Slider->SpecialInfo))
static ULONG OldSecs,OldMics; /* timers for double click */
static LISTINFO *ActiveList; /* list with active slider */
/*
* FindListPos()
*
* Assumes list is sorted (in reverse order)
* Look through the list until a match is found or until we have gone past
* the items position in the list. Set PrevItem to point to the list item
* that should preceed the specified item. Set ItemPtr to the ListItem pointer
* that needs to point to the specified item (it could be the head-of-list
* pointer iteself).
*/
void FindListPos(Name,FirstItem,PrevItem,ItemPtr)
char *Name;
LISTITEM **FirstItem;
LISTITEM **PrevItem;
LISTITEM ***ItemPtr;
{
LISTITEM *theItem = *FirstItem;
int NotFound = TRUE;
*PrevItem = NULL;
*ItemPtr = FirstItem;
while (theItem && NotFound)
{
if (stricmp(Name,theItem->Name) < 0)
{
*PrevItem = theItem;
*ItemPtr = &(theItem->Next);
theItem = theItem->Next;
} else {
NotFound = FALSE;
if (strcmp(Name,theItem->Name) == 0) *ItemPtr = NULL;
}
}
}
/*
* InitList()
*
* Clear the list and select counters
* If there are items in the list,
* count the first item
* check if it is to be selected
* While there are items following the current one
* go on to the following item
* count the new item
* if the selected item has been seen, count its position, too
* if the current item is to be selected, do so
* Set the list to point to the last (first) item in the list (the list is
* stored as an inverted list, so this item is really the first item)
* If no item was selected
* set the list to point to the top if the list and mark no selection
* otherwise
* set the top item to the selected item, and mark the selected item
* return the item at the top of the list
*/
LISTITEM *InitList(theList,theItem,SelectItem)
LISTINFO *theList;
LISTITEM *theItem,*SelectItem;
{
theList->ListLen = 0;
theList->SelectedItem = LI_NOSELECT;
if (theItem)
{
theList->ListLen++;
if (theItem == SelectItem) theList->SelectedItem = 0;
while (theItem->Next)
{
theItem = theItem->Next;
theList->ListLen++;
if (theList->SelectedItem != LI_NOSELECT) theList->SelectedItem++;
if (theItem == SelectItem) theList->SelectedItem = 0;
}
}
theList->Item = theItem;
if (theList->SelectedItem == LI_NOSELECT)
{
theList->TopItem = 0;
theList->ItemSelect = NULL;
} else {
theList->TopItem = theList->SelectedItem;
theList->SelectedItem = 0;
theItem = SelectItem;
theList->ItemSelect = SelectItem;
}
return(theItem);
}
/*
* SetText()
*
* Set the IntuiTexts pen and top edge correctly for its position
* If the text is not past the end of the list
* copy the items name into the text and pad it with spaces if necessary
* otherwise
* fill the text string with spaces (these wipe out any old text that
* may have been showing on the screen previously, the alternative would
* be to clear the entire list gadget area each time, which would make
* the gadget "flash" more).
*/
static void SetText(theList,theText,theItem,i)
LISTINFO *theList;
struct IntuiText *theText;
LISTITEM *theItem;
short i;
{
theText->BackPen = FOREGROUND;
theText->TopEdge = LNAMEH * i + 2;
if (theItem)
{
strncpy(theText->IText,theItem->Name,theList->Width);
i = theItem->NameLen; if (i == 0) i = strlen(theText->IText);
while (i < theList->Width) theText->IText[i++] = ' ';
} else {
for (i=0; i<theList->Width; i++) theText->IText[i] = ' ';
}
theText->IText[theList->Width] = 0;
}
/*
* SetTextPositions()
*
* Set the top edges of the IntuiTexts according to their positions in the
* list (when the list scrolls, the texts are not copied from one to another,
* rather, the screen positions of the texts are changed). TopText holds the
* array index of the text currently at the top - the list wraps around to the
* top of the array when it falls off the bottom.
*/
static void SetTextPositions(theList)
LISTINFO *theList;
{
struct IntuiText *theText = theList->IText;
short i,j;
j = theList->TopText;
for (i=0; i<theList->Height; i++)
{
theText[j].TopEdge = LNAMEH * i + 2;
if (j < theList->Height - 1) j++; else j = 0;
}
}
/*
* SetListNames()
*
* Set the top item in the list to the specified item
* Reset the text array to start at the beginning of the array
* For each text in the array
* set the text string to the specified item name and position
* relink the text pointers (in case they have changed)
* If there are more items in the list, go on to the next one
*/
static void SetListNames(theList,theItem)
LISTINFO *theList;
LISTITEM *theItem;
{
struct IntuiText *theText = theList->IText;
short i;
theList->ItemTop = theItem;
theList->TopText = 0;
for (i=0; i<theList->Height; i++,theText++)
{
SetText(theList,theText,theItem,i);
if (i < theList->Height-1)
theText->NextText = &(theList->IText[i+1]);
else
theText->NextText = NULL;
if (theItem) theItem = theItem->Prev;
}
}
/*
* UpdateListSelect()
*
* Find the position of the selected text in the text array
* If the selected item has changed and the new selected item is visable
* put the clear-selection image to appear behind the old selected item
* (i.e., so that it clears the previous selection, if any)
* otherwise
* put the clear-selection image so that it won't appear at all (kludge)
* If the selection is visible
* put the selection image so that it will appear behind the selected text
* link the selection image so that it appears
* set the background pen of the selected text to the selection color
* otherwise
* unlink the selection image so that it won't appear
*/
static void UpdateListSelect(theList,OldSelect)
LISTINFO *theList;
UWORD OldSelect;
{
UWORD Select = theList->SelectedItem;
short SelectText = (Select+theList->TopText) % theList->Height;
if (Select != OldSelect && OldSelect >= 0 && OldSelect < theList->Height)
theList->Image[0].TopEdge = LNAMEH * OldSelect + 1;
else
theList->Image[0].TopEdge = -(theList->Gadget->TopEdge + LNAMEH);
if (Select >= 0 && Select < theList->Height)
{
theList->Image[1].TopEdge = LNAMEH * Select + 1;
theList->Image[0].NextImage = &(theList->Image[1]);
theList->IText[SelectText].BackPen = TEXTSELECT;
} else {
theList->Image[0].NextImage = NULL;
}
}
/*
* UnsetListSelect()
*
* If the selected item follows the top item in the list
* if the selected item is visable, then reset its text pen color
* set the list's select item to a non-existant position
* if we are refreshing the list, do so
*/
void UnsetListSelect(theList,Refresh)
LISTINFO *theList;
int Refresh;
{
struct IntuiText *theText = theList->IText;
int Select = theList->SelectedItem;
int UnSelect = -(theList->TopItem + 1);
int i = (theList->TopText + Select) % theList->Height;
if (theList->SelectedItem > UnSelect)
{
if (Select >= 0 && Select < theList->Height)
theText[i].BackPen = FOREGROUND;
theList->SelectedItem = UnSelect;
if (Refresh)
{
theList->ItemSelect = NULL;
UpdateListSelect(theList,Select);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
}
}
}
/*
* UpdateListSlider()
*
* Calculate the new body and pot values according to the current list
* position and length.
* If the list's requester is active,
* if the slider's position has changed, update the gadget
* otherwise
* simply set the values of the slider
*/
static void UpdateListSlider(theList)
LISTINFO *theList;
{
int Pot,Body;
if (theList->ListLen > theList->Height)
{
Body = 0xFFFF * theList->Height / theList->ListLen;
Pot = 0xFFFF * theList->TopItem / (theList->ListLen - theList->Height);
} else {
Body = 0xFFFF;
Pot = 0;
}
if (ActiveRequest)
{
if (Body != SLIDERINFO->VertBody || Pot != SLIDERINFO->VertPot)
NewModifyProp(theList->Slider,myWindow,theList->Request,
SLIDERINFO->Flags,0,Pot,0,Body,1);
} else {
SLIDERINFO->VertBody = Body;
SLIDERINFO->VertPot = Pot;
}
}
/*
* SetList()
*
* Initialize the list info structures
* If the current position of the list (due to putting selected item at the
* top) puts the end of the list within the visable part of the list,
* move the top of the list so that the full list area is used.
* Set up the list text names
* set up the select images
* refresh the list, if necessary
* update the lider positions
*/
void SetList(theGadget,theItem,SelectItem)
struct Gadget *theGadget;
LISTITEM *theItem;
LISTITEM *SelectItem;
{
LISTINFO *theList = (LISTINFO *)theGadget->UserData;
theItem = InitList(theList,theItem,SelectItem);
while (theList->TopItem + theList->Height > theList->ListLen &&
theList->TopItem && theItem->Next)
{
theList->TopItem--; theList->SelectedItem++;
theItem = theItem->Next;
}
SetListNames(theList,theItem);
UpdateListSelect(theList,LI_NOSELECT);
if (ActiveRequest) RefreshGList(theGadget,myWindow,theList->Request,1);
UpdateListSlider(theList);
}
/*
* DoListScrollUp()
*
* While the arrow gadger is still pressed
* if there are more items to scroll
* remove the select images and text pens
* move the top of the list
* calculate the new top of list
* set the text of the new item in the list
* set up the new top item pointer and text array index
* set the IntuiText positions to their new positions
* put back the select images
* refresh the list gadget
* update the slider
*/
void DoListScrollUp(theList,theGadget,theMessage)
LISTINFO *theList;
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
struct IntuiText *theText = theList->IText;
short Top;
short OldSelect;
while (theGadget->Flags & SELECTED)
{
if (theList->TopItem)
{
OldSelect = theList->SelectedItem;
UnsetListSelect(theList,FALSE);
theList->TopItem--; theList->SelectedItem = OldSelect+1;
if (theList->TopText)
Top = theList->TopText - 1; else Top = theList->Height - 1;
SetText(theList,&(theText[Top]),theList->ItemTop->Next,0);
theList->ItemTop = theList->ItemTop->Next;
theList->TopText = Top;
SetTextPositions(theList);
UpdateListSelect(theList,OldSelect);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
UpdateListSlider(theList);
}
}
}
/*
* DoListScrollDown()
*
* Find the bottom of the visable area of the list
* While the arrow gadger is still pressed
* if there are more items to scroll
* remove the select images and text pens
* move the top of the list
* set the text of the new item in the list
* set up the new top item pointer and text array index
* set the IntuiText positions to their new positions
* put back the select images
* refresh the list gadget
* update the slider
*/
void DoListScrollDown(theList,theGadget,theMessage)
LISTINFO *theList;
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
struct IntuiText *theText = theList->IText;
LISTITEM *ItemBot = theList->ItemTop;
int count = 0;
short OldSelect = theList->SelectedItem;
while (ItemBot && count < theList->Height)
{
ItemBot = ItemBot->Prev;
count++;
}
while (theGadget->Flags & SELECTED)
{
if (theList->TopItem + theList->Height < theList->ListLen)
{
OldSelect = theList->SelectedItem;
UnsetListSelect(theList,FALSE);
theList->TopItem++; theList->SelectedItem = OldSelect - 1;
SetText(theList,&(theText[theList->TopText]),ItemBot,0);
theList->ItemTop = theList->ItemTop->Prev; ItemBot = ItemBot->Prev;
if (theList->TopText < theList->Height - 1)
theList->TopText++; else theList->TopText = 0;
SetTextPositions(theList);
UpdateListSelect(theList,OldSelect);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
UpdateListSlider(theList);
}
}
}
/*
* DoListSlider()
*
* Calculate new list position according to current slider settings
* If the position has changed
* record the old selected item
* set the new top item
* set the list name to the new position in the list
* update the selected image positions
* refresh the list gadget
*/
void DoListSlider(theList)
LISTINFO *theList;
{
LISTITEM *theItem = theList->Item;
short Top;
short OldSelect;
short Size = theList->ListLen - theList->Height;
if (Size < 0) Size = 0;
Top = Size * SLIDERINFO->VertPot / 0xFFFF;
if (Top > Size) Top = Size; else if (Top < 0) Top = 0;
if (Top != theList->TopItem)
{
OldSelect = theList->SelectedItem;
theList->SelectedItem += theList->TopItem - Top;
theList->TopItem = Top;
while (Top-- && theItem) theItem = theItem->Prev;
SetListNames(theList,theItem);
UpdateListSelect(theList,OldSelect);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
}
}
/*
* StartListSlider()
*
* If the slider's knob was hit, move to the new location, then start
* mouse moves and record the list whose slider is active.
*/
void StartListSlider(theList)
LISTINFO *theList;
{
if (SLIDERINFO->Flags & KNOBHIT)
{
DoListSlider(theList);
StartMouseMoves();
ActiveList = theList;
}
}
/*
* EndListSlider()
*
* Stop getting mouse moves and clear the active list, and show the new
* list position.
*/
void EndListSlider(theList)
LISTINFO *theList;
{
StopMouseMoves(); ActiveList = NULL;
DoListSlider(theList);
}
/*
* DoListMouse()
*
* If a list slider is active, calculate the new list position according
* to the sliders new position.
*/
void DoListMouse(MouseX,MouseY,theMessage)
short MouseX,MouseY;
struct IntuiMessage *theMessage;
{
if (ActiveList) DoListSlider(ActiveList);
}
/*
* DoListHit()
*
* Find the index of the list item that was hit
* If the position is within the list (i.e., not past then end of the list)
* Find the item within the list (starting from the top of the visable list)
* If the item was not the selected one,
* remove the selected image and pen colors
* set the new selected item
* refresh the list gadget
* set the selected item pointer
* if the list has a name gadget and we have selected a new item
* copy the item's name into the name gadget (NOFIRST is a kludge
* to keep from copying a directories leading '/')
* refresh the name
* set the double-click counters
* return no item selected (wait for a double-click)
* (otherwise with no name gadget, return the selected item)
* otherwise (the item was already selected)
* if the list has a name gadget
* if this is not a double-click,
* start a new double-click clock count, and return no item
* otherwise
* don't return the already-selected item
* Otherwise (clicked past the end of the list)
* clear the selection and the gadget name string
*/
LISTITEM *DoListHit(theList,theMessage)
LISTINFO *theList;
struct IntuiMessage *theMessage;
{
short y,i;
short OldSelect = theList->SelectedItem;
LISTITEM *theItem = NULL;
y = theMessage->MouseY - theList->Gadget->TopEdge -
theList->Request->er_Request.TopEdge - 1;
i = y / LNAMEH; y = i;
if (theList->TopItem + i < theList->ListLen)
{
theItem = theList->ItemTop;
while (y-- && theItem) theItem = theItem->Prev;
if (i != OldSelect)
{
UnsetListSelect(theList,FALSE);
theList->SelectedItem = i;
UpdateListSelect(theList,OldSelect);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
theList->ItemSelect = theItem;
if (theList->Name && theItem)
{
if (theItem->Flags & LI_NOFIRST)
SetStringInfo(theList->Name,theItem->Name+1);
else
SetStringInfo(theList->Name,theItem->Name);
RefreshGList(theList->Name,myWindow,theList->Request,1);
OldSecs = theMessage->Seconds;
OldMics = theMessage->Micros;
theItem = NULL;
}
} else {
if (theList->Name)
{
if (DoubleClick(OldSecs,OldMics,
theMessage->Seconds,theMessage->Micros) == FALSE)
{
OldSecs = theMessage->Seconds;
OldMics = theMessage->Micros;
theItem = NULL;
}
} else {
theItem = NULL;
}
}
} else if (theList->Name) {
UnsetListSelect(theList,TRUE);
SetStringInfo(theList->Name,"");
RefreshGList(theList->Name,myWindow,theList->Request,1);
}
return(theItem);
}
/*
* SelectedListItem()
*
* Return the list's selected item (hold-over from previous approach to
* selections)
*/
LISTITEM *SelectedListItem(theList)
LISTINFO *theList;
{
return(theList->ItemSelect);
}
/*
* ActivateName()
*
* If a name gadget is hit, clear any selection in its list.
* Attempt to activate the name gadget. (There is a bug in Intuition
* that makes this fail if RETURN was pressed within the name gadget
* itself)
*/
void ActivateName(theList)
LISTINFO *theList;
{
if (theList->Name)
{
UnsetListSelect(theList,TRUE);
ActivateGadget(theList->Name,myWindow,theList->Request);
}
}
/*
* ItemBeforeTop()
*
* returns TRUE if the specified item preceeds the top item in the list.
*/
int ItemBeforeTop(theItem,theTop)
LISTITEM *theItem,*theTop;
{
while (theItem != theTop && theItem) theItem = theItem->Prev;
return(theItem == theTop);
}
/*
* RemoveListSelect()
*
* If the list has a name gadget
* clear the name string and refresh
* otherwise if the item is the selected item
* if there is an item following the selected one, select it,
* otherwise set the selected item to the previous one
* otherwise
* clear the selection
*/
static void RemoveListSelect(theList,theItem)
LISTINFO *theList;
LISTITEM *theItem;
{
if (theList->Name)
{
SetStringInfo(theList->Name,"");
RefreshGList(theList->Name,myWindow,theList->Request,1);
} else if (theItem == theList->ItemSelect) {
if (theItem->Prev)
{
theList->ItemSelect = theItem->Prev;
} else {
theList->ItemSelect = theItem->Next;
theList->SelectedItem--;
}
} else {
theList->ItemSelect = NULL;
UnsetListSelect(theList,FALSE);
}
}
/*
* RemoveListItem()
*
* Reduce the list length, and unselect the items
* If the list head is the removed item, start the list at the next item
* If the item is the top one,
* If there is an item following it, make that the top
* otherwise, make the top the previous item in the list (if any)
* otherwise if the deleted item is before the top item
* decrement the list position of the top item
* if the deletion has moved the bottom of the list into the viable area
* move the list down, if possible, to use all of the list
* reset the list names and refresh the list
* update the slider
* return the currently selected item
*/
LISTITEM *RemoveListItem(theList,theItem)
LISTINFO *theList;
LISTITEM *theItem;
{
short OldSelect = theList->SelectedItem;
LISTITEM *ItemTop = theList->ItemTop;
theList->ListLen--;
RemoveListSelect(theList,theItem);
if (theItem == theList->Item) theList->Item = theItem->Prev;
if (theItem == ItemTop)
{
if (ItemTop->Prev)
{
ItemTop = ItemTop->Prev;
} else {
ItemTop = ItemTop->Next;
if (theList->TopItem) theList->TopItem--;
}
} else if (ItemBeforeTop(theItem,ItemTop)) {
theList->TopItem--;
}
if (theList->TopItem && theList->TopItem+theList->Height > theList->ListLen)
{
theList->TopItem--; theList->SelectedItem++;
ItemTop = ItemTop->Next;
}
SetListNames(theList,ItemTop);
UpdateListSelect(theList,OldSelect);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
UpdateListSlider(theList);
return(theList->ItemSelect);
}
/*
* DoListPageDown()
*
* If there are more items to show
* record the old selected position
* calculate how many items are left to scroll (clip off at the list height)
* scroll the list by the specified amount
* set the list to show the new position
* put the selection images in place
* refresh the list gadget
* update the slider to the new position
*/
void DoListPageDown(theList)
LISTINFO *theList;
{
short i,OldSelect;
LISTITEM *theItem = theList->ItemTop;
if (theList->TopItem + theList->Height < theList->ListLen)
{
OldSelect = theList->SelectedItem;
i = theList->ListLen - theList->TopItem - theList->Height;
if (i > theList->Height) i = theList->Height;
while (i--)
{
theItem = theItem->Prev;
theList->TopItem++; theList->SelectedItem--;
}
SetListNames(theList,theItem);
UpdateListSelect(theList,OldSelect);
RefreshGList(theList->Gadget,myWindow,theList->Request,1);
UpdateListSlider(theList);
}
}