home *** CD-ROM | disk | FTP | other *** search
- /*
- #
- # Apple Macintosh Developer Technical Support
- #
- # ModalList : Simple Modal Dialog and List Manager Sample Application
- #
- # ModalList.c - Main Segment
- #
- # Copyright © 1989 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions:
- # 1.00 10/89
- # 1.01 06/92
- #
- # Components:
- # ModalList.make October 1, 1989
- # ModalList.h October 1, 1989
- # ModalList.c October 1, 1989
- # ModalListInit.c June 12, 1992
- # ModalList.r October 1, 1989
- # TCModalList.π June 12, 1992
- # TCModalList.π.rsrc June 12, 1992
- #
- # ModalList is an example application that demonstrates
- # how to use the Dialog Manager and List Manager routines
- # together. It is not a good example of a sample application
- # but a great example of the use of lists in a dialog. The
- # default LDEF is used to display a 2 dimensional list of strings.
- # Each cell's string is initialized to represent it's position in the
- # list. The user can change these strings and search for a particular
- # setting. Once again this is not meant as an example application and
- # there are some features that are documented in the source listing
- # that you should pay close attention to inorder to understand how
- # this example works.
- #
- */
-
- #pragma segment Main
-
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Controls.h>
- #include <Dialogs.h>
- #include <Windows.h>
- #include <TextEdit.h>
- #include <Memory.h>
- #include <Lists.h>
- #include <SegLoad.h>
- #include <Packages.h>
- #include <TextUtils.h>
- #include "ModalList.h"
-
- /* Here are the prototypes.
- */
-
- Boolean MyList(void); /* Modal Dialog loop */
-
- pascal void MyItem(DialogPtr, short); /* Dialog Manger call backs */
- pascal Boolean MyFilter(DialogPtr, EventRecord *, short *);
-
- pascal Boolean MyListClickLoop(void); /* List Manger call backs */
- pascal short MySearch(Ptr, Ptr, short, short);
-
- void LDeselect(ListHandle); /* utility routines */
- long Cell2String(Point, char *);
-
- /* _DataInit() is part of the MPW runtime library. This external
- ** reference to it is done so that its segment can be unloaded, %A5Init.
- */
- #ifndef THINK_C
- extern void _DataInit();
- #endif
-
- main()
- {
- #ifndef THINK_C
- UnloadSeg((Ptr) _DataInit); /* note that _DataInit must not be in Main! */
- #endif
-
- MaxApplZone(); /* expand the heap so code segments load at the top */
-
- Initialize();
- UnloadSeg((Ptr) Initialize); /* note that Initialize must not be in Main! */
-
- (void) MyList(); /* this sample ignores the return value */
- }
-
-
- /* a utility routine to deselect all selected cells in the list.
- */
-
- void LDeselect(ListHandle list)
- {
- auto Point cell;
-
- /* start with the upper left cell
- */
- SetPt(&cell, 0, 0);
-
- /* for each selected cell
- */
- while(LGetSelect(true, &cell, list))
-
- /* deselect and unhilite it
- */
- LSetSelect(false, cell, list);
- }
-
- /* a click loop procedure that does nothing but prove it can be done.
- */
-
- pascal Boolean MyListClickLoop(void)
- {
- return(true);
- }
-
- /* a custom search procedure that is case sensitive. the standard list search procedure
- ** uses IUMagIDString which is case insensitive.
- ** Parameters: Ptr aPtr; the cell data
- ** Ptr bPtr; the search pattern
- ** short aLen, bLen; the lengths
- */
-
- pascal short MySearch(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
- {
- return (0 != IUMagString(aPtr, bPtr, aLen, bLen));
- }
-
- /* this routine will be called by the Dialog Manager to draw the outline of the
- ** default button and the list.
- */
-
- pascal void MyItem(DialogPtr dialog, short itemNo)
- {
- auto short itemType;
- auto Handle itemHandle;
- auto Rect itemRect;
-
- auto ListHandle list;
-
- GetDialogItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
- switch(itemNo) {
-
- /* outline the default button (see IM I-407). in this case it is the OK button.
- ** this lets the user know that pressing the return will have the same effect as
- ** clicking this button.
- */
- case cOKOutlineItem :
- PenSize(3, 3);
- InsetRect(&itemRect, -4, -4);
- FrameRoundRect(&itemRect, 16, 16);
- PenSize(1, 1);
- break;
-
- /* draw the list. the List Manager will draw the cells and scrollbars but does not
- ** draw a border around the list's view rectangle, so it is done here also.
- */
- case cListItem :
-
- /* recover the list handle. it was stuffed into the dialog window's refCon
- ** field when is was created.
- */
- list = (ListHandle) ((DialogPeek)dialog)->window.refCon;
-
- /* let the List Manager draw the list
- */
- LUpdate(dialog->visRgn, list);
-
- /* drawn the lists framing rectangle OUTSIDE the view rectangle.
- ** if the frame is drawn inside the view rectangle then these lines
- ** will be erased, drawn onto or scrolled by the List Manager since the lines
- ** are within the rectangle LM expects to be able to draw in.
- */
- InsetRect(&itemRect, -1, -1);
- FrameRect(&itemRect);
- break;
- }
- }
-
- /* we need to be able to process mouse clicks in the list. the Dialog Manager makes this
- ** possible through filter procedures like this one. since the default filter procedure
- ** will be replaced we also need to handle return key presses.
- */
-
- pascal Boolean MyFilter(DialogPtr dialog, EventRecord *event, short *itemHit)
- {
- auto ListHandle list;
- auto Point cell;
- auto char character;
- auto Point where;
- auto Rect itemRect;
- auto short itemType;
- auto Handle itemHandle;
- auto Str255 string;
- auto short length;
-
- switch (event->what) {
-
- /* watch for mouse clicks in the List
- */
- case mouseDown :
- GetDialogItem(dialog, cListItem, &itemType, &itemHandle, &itemRect);
- where = event->where;
- GlobalToLocal(&where);
-
- /* if the user has clicked in the list then we'll handle the processing here
- */
- if (PtInRect(where, &itemRect)) {
-
- /* recover the list handle. it was stuffed into the dialog window's refCon
- ** field when it was created.
- */
- list = (ListHandle) ((DialogPeek)dialog)->window.refCon;
-
- /* let the List Manager process the mouse down. this includes cell selection
- ** dragging, scrolling and double clicks by the user.
- */
- if (LClick(where, event->modifiers, list)) {
-
- /* a double click in a cell has occured.
- ** find out in which one of the cells the user has double clicked in.
- */
- cell = LLastClick(list);
-
- /* copy this cell's contents to the text edit box in the dialog
- */
- GetDialogItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
- length = 255;
- LGetCell(&string[1], &length, cell, list);
- string[0] = (char) length;
- SetDialogItemText(itemHandle, string);
- }
-
- /* tell the application that the list has been clicked in.
- */
- *itemHit = cListItem;
-
- /* tell the Dialog Manager that the event has been handled.
- */
- return true;
- }
- break;
-
- /* be sure and return this information so the Dialog Manager will process the
- ** return and enter key presses as clicks by the user in the OK button. this is
- ** only required because we have overridden the Dialog Manager's default filtering.
- */
- case keyDown :
- case autoKey :
- character = event->message;
- if ('\n' == character) {
-
- /* tell the application that the OK button has been clicked by the user.
- */
- *itemHit = ok;
-
- /* tell the Dialog Manger that the event has been handled and the
- ** character should not be added to the text edit field.
- */
- return true;
- }
- break;
- }
-
- /* tell the Dialog Manger that the event has NOT been handled and that it should
- ** take further action on this event.
- */
- return false;
- }
-
- /* this routine will not return until the user has dismissed the modal dialog with
- ** a press of the return key or a click in the ok or cancel buttons. it displays a
- ** list, check boxes for setting selection flags, a text edit field for searching and setting
- ** cell data and the usual ok and cancel buttons. the list uses the default LDEF procedure.
- **
- ** if you double click in a cell the cells contents will be moved to the
- ** text edit field. edit the field and click the set button and you will have edited the
- ** cell contents.
- **
- ** note that the Set button sets the last cell clicked in to the contents of the text edit field.
- ** it does not necessarily set the cell which is selected or hilited but it sets the cell that
- ** LLastClick returns. it is sort of a funky user interface but it does demonstrate the
- ** use of LLastClick.
- **
- ** if you scroll the list all the way to the right you will notice that there is some extra
- ** white space to the right of the right most cell. this was done intentionally to show what
- ** can happen when the lists viewing rectangle is not an even multiple of the cell rectangles.
- ** the List Manager must scroll this extra bit so that it can display the entire width of the
- ** right most cells. notice too that the bottom most cell does not display this behavior since
- ** the height of the list's viewing rectangle is an even multiple of the cell height.
- **
- ** note that a call to LSetSelect will select or hilite a cell which is empty even though
- ** the list's selection flags field is set to lNoNilHilite. the selection flags
- ** are used only by the LClick routine they do not prevent the program from selecting
- ** cells with LSetSelect in such a way that may seem inconsistent with their
- ** setting. this peculiarity can also happen with other selection flag settings and the
- ** LSetSelect call.
- **
- ** notice also that the column widths are all equal, there is no way to make cells with different
- ** column widths using the List Manager.
- **
- ** there have been requests for the capability to have multiple lists displayed in a
- ** window and have them both scrolled by one scrollbar. the only way to accomplish this
- ** would be to make new lists without the List Manager's scrollV or scrollH scrollbars.
- ** then create a new scrollbar using the Control Manager and setup an action procedure for
- ** TrackControl which calls LScroll for each of the lists. this is not demonstrated in
- ** this sample program.
- **
- */
-
- RoutineDescriptor MyListClickLoopRD = BUILD_ROUTINE_DESCRIPTOR (uppListClickLoopProcInfo, MyListClickLoop);
- // LClickLoopGlue is a quick & dirty way of doing some inline 68K assembly from PowerPC code.
- // We need to do this because if we have the List Manager call our native click loop directly,
- // then it can fail because the List Manager doesn't actually test the result in D0, it just
- // checks the state of the Z-bit, which Mixed Mode doesn't set for us.
- #ifdef powerc
- #pragma options align=mac68k
- #endif
- struct LClickLoopGlue {
- long move; // MOVEA.L ClickLoopUPP, A0
- short jsr; // JSR (A0)
- short tst; // TST.B D0
- short rts; // RTS
- UniversalProcPtr ClickLoopUPP; // Storage for the UPP
- } LClickLoop68K = {
- 0x207A0008,
- 0x4E90,
- 0x4A00,
- 0x4E75,
- &MyListClickLoopRD
- };
- #ifdef powerc
- #pragma options align=reset
- #endif
-
- Boolean MyList(void)
- {
- auto DialogPtr dialog; /* a dialog containing the list item */
-
- auto short itemNo; /* the item in the dialog selected */
- auto short itemType; /* dummy parameter for call to GetDialogItem */
- auto Handle itemHandle; /* dummy parameter for call to GetDialogItem */
- auto Rect itemRect; /* the location of the list in the dialog */
-
- auto ListHandle list; /* the list constructed in the dialog */
- auto Rect dataBounds; /* the dimensions of the data in the list */
- auto Point cellSize; /* width and height of a cells rectangle */
- auto Point cell; /* an index through the list */
-
- auto char string[255];
- auto short length;
-
- auto short checked; /* flag for setting and getting check box value */
- auto short bit; /* used as a mask to test the selection flags */
-
- auto ModalFilterUPP mfUPP;
-
- /* create a new dialog window
- */
- dialog = GetNewDialog(cDLOGID, (Ptr) 0, (WindowPtr) -1);
- SetPort((GrafPtr) dialog);
-
- /* set the procedure pointer for the user items in the dialog.
- ** this will allow he default button to be outlined and the list to be drawn
- ** by the Dialog Manger.
- */
- GetDialogItem(dialog, cOKOutlineItem, &itemType, &itemHandle, &itemRect);
- SetDialogItem(dialog, cOKOutlineItem, itemType, (Handle) MyItem, &itemRect);
-
- GetDialogItem(dialog, cListItem, &itemType, &itemHandle, &itemRect);
- SetDialogItem(dialog, cListItem, itemType, (Handle) MyItem, &itemRect);
-
- /* make room for scroll bars (see IM IV-270)
- */
- itemRect.right -= 15;
- itemRect.bottom -= 15;
-
- /* create a list
- */
- SetRect(&dataBounds, 0, 0, cListCols, cListRows);
- SetPt(&cellSize, cListCellWidth, cListCellHeight);
- list = LNew(&itemRect, &dataBounds, cellSize, 0, (WindowPtr) dialog, false, false, true, true);
-
- /* allow the dialog manager routines to access the list record
- */
- ((DialogPeek)dialog)->window.refCon = (long) list;
-
- /* use the custom click loop procedure
- */
- (*list)->lClickLoop = (ListClickLoopUPP)&LClickLoop68K;
-
- /* use the default selection flags
- */
- (*list)->selFlags = 0;
-
- /* initialize the check boxes in the dialog according
- ** to the settings of the list's selection flags
- */
- bit = 2;
- itemNo = cNoNilHiliteItem;
- while (itemNo <= cOnlyOneItem) {
-
- GetDialogItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
- checked = ((*list)->selFlags == ((*list)->selFlags | bit)) ? 1 : 0;
- SetControlValue((ControlHandle) itemHandle, checked);
-
- bit *= 2;
- ++itemNo;
- }
-
- /* initialize the cell's contents.
- */
- for (cell.v = 0; cell.v < (**list).dataBounds.bottom; ++cell.v) {
- for (cell.h = 0; cell.h < (**list).dataBounds.right; ++cell.h) {
-
- /* make a string representing the cell's position in the list.
- */
- length = Cell2String(cell, string);
-
- /* initialize the cell contents to this string.
- **
- ** you would initialize your cell data with what ever is appropriate
- ** for your application here.
- */
- LSetCell(string, length, cell, list);
- }
- }
-
- /* turn cell drawing on only after the cell contents have been initialized.
- ** this will avoid watching the delay between the LSetCells calls and is faster.
- */
- LSetDrawingMode(true, list);
-
- mfUPP = NewModalFilterProc(MyFilter);
- do {
-
- /* process hits in the dialog.
- */
- ModalDialog(mfUPP, &itemNo);
-
- switch(itemNo) {
-
- /* process hits in the OK button.
- */
- case ok :
-
- /* find out which cells have been selected.
- */
- SetPt(&cell, 0, 0);
- while(LGetSelect(true, &cell, list)) {
-
- /* there is nothing to do with the user's selections in this sample
- ** so i'll just deselect the cells the users has selected.
- */
- LSetSelect(false, cell, list);
- }
- break;
-
- /* process hits in the Find button.
- */
- case cFindItem :
-
- GetDialogItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
- GetDialogItemText(itemHandle, string);
-
- /* search for a match in the list.
- ** start the search at the upper left most cell in the list.
- **
- ** the List Manger performs the searchs row by row until it finds
- ** the first match. this order can not be changed without writing a
- ** replacement to LSearch, probably making use of LGetCell, LNextCell and
- ** the International search routines.
- */
- SetPt(&cell, 0, 0);
-
- /* deselect all cells which are currently selected.
- */
- LDeselect(list);
-
- /* use the custom search procedure.
- ** the default search procedure is not case sensitive.
- */
- if (LSearch(&string[1], string[0], MySearch, &cell, list)) {
-
- /* hilite the cell which contains the matching string.
- **
- ** note that this call will select or hilite a cell which is empty
- ** when the users search string is empty even though the list's
- ** selection flags field is set to lNoNilHilite. the selection flags
- ** are used only by the LClick routine they do not prevent the program
- ** from selecting cells with LSetSelect in such a way that may seem
- ** inconsistent with their setting. this peculiarity can also happen
- ** with other selection flag settings and the LSetSelect call.
- */
- LSetSelect(true, cell, list);
-
- /* if the matching cell is not visible it will be scrolled into view.
- */
- LAutoScroll(list);
- }
- break;
-
- /* process hits in the Set button.
- */
- case cSetItem :
- GetDialogItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
- GetDialogItemText(itemHandle, string);
-
- /* find out which was the last cell clicked in by the user.
- ** not the last cell dragged into, or the last cell selected but the
- ** last cell a mouse down event occured in.
- */
- cell = LLastClick(list);
- if (0 <= cell.h && 0 <= cell.v) {
-
-
- /* if a cell has been clicked in then deselect all cells which are
- ** currently selected.
- */
- LDeselect(list);
-
- /* set the cell to the string the user has entered.
- */
- LSetCell(&string[1], string[0], cell, list);
-
- /* notice that this will Hilite an empty cell even with
- ** the list's selection flags set to lNoNilHilite.
- */
- LSetSelect(true, cell, list);
-
- /* if the last cell clicked in is not visible it will be scrolled
- ** into view.
- */
- LAutoScroll(list);
- }
- break;
-
- /* process hits in the check boxes representing the list's selection flags.
- */
- case cOnlyOneItem :
- case cExtendDragItem :
- case cNoDisjointItem :
- case cNoExtendItem :
- case cNoRectItem :
- case cUseSenseItem :
- case cNoNilHiliteItem :
-
- /* deselect all cells which are currently selected.
- */
- LDeselect(list);
-
- /* toggle the check box.
- */
- GetDialogItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
- checked = GetControlValue((ControlHandle) itemHandle);
- SetControlValue((ControlHandle) itemHandle, !checked);
-
- /* adjust the selection flags to the users new settings.
- */
- switch (itemNo) {
- case cOnlyOneItem :
- (*list)->selFlags ^= lOnlyOne; break;
- case cExtendDragItem :
- (*list)->selFlags ^= lExtendDrag; break;
- case cNoDisjointItem :
- (*list)->selFlags ^= lNoDisjoint; break;
- case cNoExtendItem :
- (*list)->selFlags ^= lNoExtend; break;
- case cNoRectItem :
- (*list)->selFlags ^= lNoRect; break;
- case cUseSenseItem :
- (*list)->selFlags ^= lUseSense; break;
- case cNoNilHiliteItem :
- (*list)->selFlags ^= lNoNilHilite; break;
- }
-
- break;
- }
- } while ((itemNo != ok) && (itemNo != cancel));
- DisposeRoutineDescriptor(mfUPP);
-
- /* kill the list and dialog.
- */
- LDispose(list);
- DisposeDialog(dialog);
-
- /* return true if OK or RETURN key was hit.
- */
- return (ok == itemNo);
- }
-
- /* just a funky routine to create strings for the cell's data
- */
-
- long Cell2String(Point pt, char *string)
- {
- auto short index;
- auto Str255 rowStr;
- auto Str255 colStr;
-
- NumToString((long)pt.v, rowStr);
- *(string)++ = 'R';
- for (index = 1; index <= rowStr[0]; ++index)
- *(string++) = rowStr[index];
-
- *(string)++ = ',';
- *(string)++ = ' ';
-
- NumToString((long)pt.h, colStr);
- *(string)++ = 'C';
- for (index = 1; index <= colStr[0]; ++index)
- *(string++) = colStr[index];
-
- return colStr[0] + rowStr[0] + 4;
- }
-