*******************************************************************************
*
* fakeModalDialog and other supporting access routines and such.
* Version 3.0
*
* Copyright (c)
* Apple Computer, Inc.  1989-1990
* All Rights Reserved.
*
* Written by Eric Soldan.
*
* Developer Technical Support Apple II Sample Code
*
* The purpose of this code is to handle dialogs in a variety of ways
* that are more robust than what the toolbox offers.  It also supports
* the new movable/modeless dialog types described in the latest human
* interface guidelines.
*
*******************************************************************************
*
* Here is some C sample code that uses fakeModalDialog.  This code is
* for an about box.  What is different about this about box is that
* you can use menus while the about box is on the screen.  This means
* that you can use desk accessories while the about box is active.
* It also means that you can quit the application at this time.
* You can also close the about box with the close menu option.
*
* This sample uses the modal mode with menus for the about box.  Since
* menus are available, there is more support (code) necessary from the
* application.  The application has to detect that the user chose
* close from the menu and take appropriate action, for example.
*
* First, a little about fakeModalDialog:
*
* fakeModalDialog returns a long as a result.
* If the result is NULL:
*     Nothing happened.  Unlike ModalDialog, control is returned to
*     the application during null events.  This is so the application
*     can do stuff like switch cursors, or other idle tasks.
* If bit 31 of the result is true:
*     The user chose a menu item.  Bits 0-15 are the menu item ID.
*                                  Bits 16-30 are the menu ID.
* If bit 31 of the result is false:
*     The user clicked on a control.  Bits 0-30 are the control ID.

* Now, the sample code fragment:

*   #define aboutBoxID 0x1001L
*
*   void    doAboutBox()
*   {
*       WindowPtr       wptr, keepPort;
*       unsigned long   id;
*       unsigned int    item;
*       keepPort = GetPort();   /* Preserve the current port. */
*
*       wptr = NewWindow2(NULL, NULL, NULL, NULL, 2, aboutBoxID, rWindParam1);
*           /* Open the about box from a resource. */
*
*       if (!_toolErr) {            /* If the window opened successfully... */
*           aboutWindow = wptr;     /* Set the aboutWindow global variable. */
*                                   /* This tells other routines what state */
*                                   /* we are in.                           */
*
*           for (;;) {      /* Loop, due to NULL events being returned. */
*
*               id = fakeModalDialog(
*                   &event,         /* Pointer to extended event record.   */
*                   NULL,           /* Use default update procedure.       */
*                   NULL,           /* There will be no event mods.        */
*                   NULL,           /* Do standard beep.                   */
*                   fmdMenuSelect+  /* Allow pullDowns.                    */
*                   fmdMenuKey+     /* Allow menu keys.                    */
*                   fmdDeskAcc+     /* Allow desk accessories.             */
*                   fmdUpdateAll    /* Update all windows, not just about. */
*               );
*
*               item = id;          /* Get lo-word from result. */
*
*               if (id & 0x80000000L) {             /* If menu command...    */
*                   if (item == 255) {              /* If close command then */
*                       HiliteMenu(0, FileMenuID);  /* turn menu inv. off.   */
*                       break;                      /* Go close about box.   */
*                   }
*                   doMenuCommand(id);      /* Do other menus (like quit).   */
*                   if (quitFlag) break;    /* User did do a quit from menu. */
*               }
*               else {                          /* ...else handle controls. */
*                   if (item == 1) break;       /* User clicked OK button.  */
*               }
*           }
*
*           SetPort(keepPort);      /* Put port back before closing about. */
*           CloseWindow(wptr);      /* Close aboutBox.                     */
*           aboutWindow = NULL;     /* Let others know it is closed.       */
*           appSetMenus();          /* Update the menus to reflect a new.  */
*       }                           /* top window.                         */
*   }

* An interesting point here is the call to appSetMenus.  This is a routine
* in the application that is responsible for putting the menus in the
* correct state.  fakeModalDialog automatically calls this routine (if told
* to) whenever an activate event occurs that it gets a chance to see.
* fakeModalDialog may not see the activate event  only if a desk accessory
* is open, just behind the about box.  When the about box is closed, the
* desk accessory gets the activate event.  fakeModalDialog never got an
* activate event, so it didn't know to call the menu update routine.  This
* situation can only occur when a desk accessory is just behind the window
* to be closed.  As a safe rule, whenever a window is closed, the menu
* update routine should be called by the application.  (Something like this
* should be done anyway.)

* To tell fakeModalDialog to automatically call a menu update routine when
* it gets an activate event, make a call to fmdSetMenuProc.  For an application
* menu update routine called appSetMenus, you would tell fakeModalDialog
* about it by the following:
*     fmdSetMenuProc(appSetMenus);
* To turn off menu updating again, do this:
*     fmdSetMenuProc(NULL);

* fmdEditMenu figures the state of cut, copy, paste, and clear in
* the edit menu based on the state of controls in the window.
* It looks up the target control (either lineEdit or textEdit) and
* then sets the states appropriately.  For lineEdit, if anything
* is selected, then cut, copy and clear are available.  If there is
* anything in the scrap, then paste is also available.  TextEdit 
* works the same way, unless the textEdit control is a read-only
* control.  In that case, cut, paste, and clear are not available.
* If there is no lineEdit or textEdit control, then the state of
* these menu items is left alone.  The application needs to take care
* of them in that case.  (It is possible that there are custom
* controls for the application that can use these menu items, and
* there is no way for fakeModalDialog to know about them.  Therefore
* the correct thing to do is to do nothing.)

* fmdFindCursorCtl is used to find which control is at a certain point.
* The toolbox call FindControl can't be used for this because textEdit
* controls become active when called with a point in them.  That is not
* the desired effect of fmdFindCursorCtl.  If you just want to know what
* control you are over and have nothing affected, then use this entry
* point.  It is used by fakeModalDialog to determine whether or not to
* display an i-beam cursor.  If the i-beam bit is set and the user moves
* the mouse over a lineEdit or textEdit control, then the cursor changes
* to an i-beam.  Note that the scrollbars for a textEdit control are
* considered a separate control.  This is so that the cursor stays
* an arrow when over the scrollbars.

* Back to fakeModalDialog:

* Some sample calls (in C) would look like:
*   ctlID = fakeModalDialog(&event, NULL, NULL, NULL, 0);
*   ctlID = fakeModalDialog(&event, updateProc, NULL, -1L, 0);
*   ctlID = fakeModalDialog(&event, updateProc, eventHook, beepProc, flags);
*
* The parameters are:
*
* event:          A pointer to an extended (for 5.0) event record.
* updateProc:     Pointer to function to update the window.  Pass a NULL for
*                 default.  (The default entry point is called fmdStdDrawProc.)
* eventHook:      Pointer to function that is called after the GetNextEvent
*                 call and before any processing occurs on that event.  This
*                 is more useful than a filter.  You can simply change the
*                 event by changing the event record.  For example:  Let's say
*                 that you want to be able to cancel a modal dialog by pressing
*                 either openApple-period or ESC.  The problem is that,
*                 although you can have two key equivalents for a
*                 super-control, theway that modifiers are determined is the
*                 same for both keys.  This means that you can't have
*                 openApple-period and ESC asyour two key equivalents.  With
*                 this event hook, you can lookat the event, and if it is the
*                 openApple-period, you can simply change the event record so
*                 that it is an ESC with no openApple modifier.  Then when you
*                 return to fakeModalDialog, it will handle the event as if the
*                 user simply typed an ESC.  By using this technique, you can
*                 have as many modifiers as you want.  If you wish to filter
*                 out an event, you would simply changethe event type to a
*                 NULL event.
* beepProc:       Pointer to custom beep function.  fakeModalDialog calls this
*                 if there is any mouseDown outside the window, just like
*                 ModalDialog.If you wish it to beep like ModalDialog, pass a
*                 NULL.  If you wish it to do nothing, pass a -1.
*                 If you wish a custom beep function, pass it a pointer to
*                 that function.
* flags, bit 0:   Cut/Copy/Paste for lineEdit items would normally involve
*                 the scrap.  When this bit is true, the scrap is not involved
*                 when using lineEdit items.  This may be useful to protect a
*                 large scrap for an application from being hit with something
*                 trivial.
*        bit 1:   If true, allow menu pullDowns for movable/modal dialogs.
*                 If they are allowed, then the return value is the menu
*                 item ID (low-word) and the menu ID (hi-word).  To
*                 distinguish from ctlID's, thehi-bit (bit 31) is set.  This
*                 means that if you are allowing menu selections, you can't have
*                 any menu ID's with a hi-bit-on ID.  It also means that you
*                 can't have controls with bit 31 on either.
*        bit 2:   If true, allow menu keys.  Return values work the same as for
*                 pullDowns.
*        bit 3:   If true, use i-beam cursor for lineEdit & textEdit ctls.
*                 Otherwise, use an arrow cursor everywhere.
*        bit 4:   If true, automatically handle desk accessories.  This means
*                 that bit 1 must be true to have fakeModalDialog open the DA.
*                 If the DA is already open, the user can click on it, and it
*                 will become active, even if bit 1 is false.
*        bit 14:  If true, update all windows.  The toolbox call ModalDialog
*                 does not update other windows.  This takes away a lot of the
*                 point of movable/modal dialogs.  The whole point is so that
*                 you can see what is behind it.  When you move the dialog, if
*                 the windows in back didn't update, then there would be no
*                 point in moving the window.  If you want, you can set this
*                 bit false, and it will update only the front-most
*                 application window.
*        bit 15:  This is the bit that determines whether or not a dialog is
*                 movable.  If it is true, then the dialog is movable.  To be
*                 moved, however, it has to have a title bar (the drag region)
*                 and it has to have the fMove bit set.  Without these also,
*                 the window still won't be able to be moved.
*                 
* ctlID:          Control that was hit, or menu that was chosen.
*                 bit 31 off, it is a control ID.
*                 bit 31 on, it is a menu ID (hi-word), menu item ID (lo-word).
*
* There is one more thing to worry about, and that is the border of the window.
* The window manager doesn't allow you have an alert frame and a title at the
* same time.  If you set both of these bits in the wFrameBits field, then the
* alert border is drawn wrong.  It would be ideal for fakeModalDialog if this
* restriction didn't exist.  Then the window attributes all could be chosen via
* the window frame bits in a resource.  Unfortunately, this isn't the case.  To
* manage this restriction, the default update procedure, under certain
* conditions, draws the alert frame itself.  If fAlert is set, then the alert
* border isn't drawn by the default update procedure.  The update procedure
* doesn't have to draw it, since it is automatically drawn by the window
* manager.  If fAlert is set, then the dialog doesn't have a title bar, due to
* the window manager restriction.  If fAlert isn't set, then the default update
* procedure draws the alert border.  The alert border is drawn as part of the
* content of the window.  Due to this, the coordinates you use for the content
* of the window have to be a little different, to make space for the border.
* The upper-left-hand corner of the window is really 10,4, (4,4 for 320 mode)
* instead of 0,0.  So far, we have discussed how the alert border gets drawn,
* either by the window manager, or by the default update procedure for
* fakeModalDialog.  It is possible that you don't want an alert border at all.
* If you don't, then fAlert should be off, and you should turn on fFlex.  fFlex
* was used because it should be able to be set from a resource, and all other
* bits would have an adverse effect.  Dialogs don't have grow boxes and zoom
* boxes, so fFlex is meaningless for dialogs (until now).

***********************************************************************

	case on

	INCLUDE 'm16.util2'
	INCLUDE 'e16.types'

	INCLUDE 'e16.control'
	INCLUDE 'm16.control'
	INCLUDE 'm16.desk'
	INCLUDE 'e16.event'
	INCLUDE 'm16.event'
	INCLUDE 'e16.lineedit'
	INCLUDE 'm16.lineedit'
	INCLUDE 'm16.memory'
	INCLUDE 'm16.menu'
	INCLUDE 'm16.misctool'
	INCLUDE 'm16.quickdraw'
	INCLUDE 'm16.scrap'
	INCLUDE 'm16.textedit'
	INCLUDE 'e16.window'
	INCLUDE 'm16.window'

	longi on
	longa on

***********************************************************************

fmdNoScrapForLE	equ $0001
fmdMenuSelect	equ $0002
fmdMenuKey	equ $0004
fmdIBeam	equ $0008
fmdDeskAcc	equ $0010
fmdUpdateAll	equ $4000
fmdMovable	equ $8000

	export fakeModalDialog
fakeModalDialog	PROC
	export FAKEMODALDIALOG
	export fmdGetCtlPart, FMDGETCTLPART
	import fmdEditMenu, fmdFindCursorCtl
	import fmdStdDrawProc, fmdGetMenuProc
	import fmdIBeamCursor, fmdGetIBeamAdr
	import _fmdNoError

	DefineStack

dlgwptr	long	;Must be at 1,s
keepPort	long	;Must be at 5,s
ctlHndl	long
ctlPtr	long
theHndl	long
thePtr	long
whichWindow	long
tempwptr	long
code	word
editTask	word
yloc	word
xloc	word
wkind	word
menunum	word
zero	word

sizeLocals	EndLocals

saveDPage	word
returnAddr	block 3

	BegParms
flags	word	;bit 0 = 0, involve scrap for
		;           lineEdit items.
		;bit 0 = 1, don't involve scrap for
		;           lineEdit items.
		;bit 1 = 0, don't do _MenuSelect for
		;           movable/modal dialogs.
		;bit 1 = 1, do _MenuSelect for
		;           movable/modal dialogs.
		;bit 2 = 0, don't do _MenuKey for
		;           movable/modal dialogs.
		;bit 2 = 1, do _MenuKey for
		;           movable/modal dialogs.
		;bit 3 = 0, use arrow cursor
		;           everywhere.
		;bit 3 = 1, use i-beam cursor for
		;           lineEdit & textEdit ctls.
		;bit 4 = 0, let app handle desk
		;           accessories.
		;bit 4 = 1, automatically handle desk
		;           accessories.
		;bit 14 = 0, Don't update other
		;            application windows.
		;bit 14 = 1, Update all windows.
		;bit 15 = 0, Modal window.
		;bit 15 = 1, movable/modal window.
beepProc	long	;Null, SysBeep.  Negative, do nothing.
eventHook	long	;Optional -- pass a NULL for none.
updateProc	long	;Optional -- pass a NULL for default.
event	long	;Must be extended 5.0 event rec ptr.
sizeParms	EndParms

retval	long	;Control ID hit.  NULL if none.
		;If bit 31 is on, then retval
		;is a menu ID.
***

FAKEMODALDIALOG	phd	;Save directPage register.
	tsc	;Make space for locals.
	sec
	sbc #sizeLocals
	tcs
	tcd	;Set directPage register.

	pla
	pla
	_GetPort	;Result space already there (keepPort).

	pha
	pha
	_FrontWindow	;Result space already there (dlgwptr).

	pha	;Find out if it is a DA window.
	pei dlgwptr+2
	pei dlgwptr
	_GetWKind
	pla
	sta wkind
	bpl @gotit

@get1stRegWnd	pha	;Result space for _GetWFrame.

	pha	;Result space for _GetNextWindow.
	pha

	pei dlgwptr+2
	pei dlgwptr
	_GetNextWindow

	lda 1,s	;Param for _GetWFrame.
	sta dlgwptr
	lda 3,s
	sta dlgwptr+2

	_GetWFrame
	pla

	and #fVis
	beq @get1stRegWnd	;Window is invisible, so skip it.

	pha
	pei dlgwptr+2
	pei dlgwptr
	_GetWKind
	pla
	bmi @get1stRegWnd	;Window is system window, so skip it.

@gotit	stz retval	;Assume nothing interesting is
	stz retval+2	;going to happen.
	stz code
	stz editTask	;For cut/copy/paste/clear signal.
	stz zero	;For optimization purposes.

	lda updateProc+1	;Set up the update procedure for all
	bne @a	;cases.  This also sets it up so that
		;TaskMaster can handle this window
		;from other parts of the application.

	lda #fmdStdDrawProc>>16
	sta updateProc+2
	lda #fmdStdDrawProc
	sta updateProc	;Use the default update procedure.
@a	pei updateProc+2
	pei updateProc
	pei dlgwptr+2
	pei dlgwptr
	_SetContentDraw	;Let the window and TaskMaster know.

@aa	pha	;Make sure that top window is
	pha	;the active window.
	_GetPort
	pla
	sta keepPort
	pla
	sta keepPort+2
	pei dlgwptr+2
	pei dlgwptr
	_SetPort

	lda flags
	and #fmdIBeam
	beq @b	;No cursor changes.

	lda wkind
	bmi @b

	pha	;Result space for control part.

	ldy #0	;Point to where to put the handle.
	phy
	tdc
	clc
	adc #ctlHndl
	pha	;Pointer to ctlHndl now on stack.

	phy
	adc #yloc-ctlHndl
	pha	;Pointer to yloc now on stack.
	_GetMouse

	pei xloc	;Now that they are right, pass 'em on.
	pei yloc
	pei dlgwptr+2
	pei dlgwptr
	jsl fmdFindCursorCtl ;Find which control cursor is over.
	pla	;The part code, which we don't care
		;about here.

	beq @arrowCursor	;Not over any control -- use arrow.

	jsr getCtlProc	;Find out what kind of control it is.
	cmp #editTextControl>>16
	beq @ibeamCursor	;It is a TextEdit ctl, kind of
		;See fmdFindCursorCtl for more info.
	cmp #editLineControl>>16
	beq @ibeamCursor	;It is a LineEdit tool.

@arrowCursor	jsr ibeamTest
	bcc @b
	_InitCursor
	bra @b	;We have an arrow cursor.

@ibeamCursor	jsr ibeamTest
	bcs @b	;We have an ibeam cursor.
	jsl fmdIBeamCursor

@b	_SystemTask

	lda flags	;See if cut/copy/paste is tied to menu.
	and #fmdMenuSelect+fmdMenuKey
	beq @c	;Isn't keyDownEvt or mouseDownEvt.
	jsl fmdEditMenu	;Update the edit menu.

@c	pha	;Get the event.
	pea $FFFF
	pei event+2
	pei event
	_GetNextEvent
	pla
	beq @exit	;Event shouldn't be handled by app.

	ldy #owhat
	lda [event],y
	sta code
	bne @gotEvent

@exit	lda code	;See if code is 0.  If it is, force a
	bne @exit0	;NULL event, so the cursor will flash
	ldy #owhat	;for lineedit items, etc.
	sta [event],y
	pha
	pea 1
	pha
	pha
	pei event+2
	pei event
	_SendEventToCtl
	pla

@exit0	pei keepPort+2
	pei keepPort
	_SetPort
	tsc	;Get rid of local variables.
	clc
	adc #sizeLocals
	tcs
	pld	;Restore directPage register.
	lda 1,s	;Move return address.
	sta 1+sizeParms,s
	lda 2,s
	sta 2+sizeParms,s
	tsc	;Get rid of passed parameters.
	adc #sizeParms
	tcs
	jml _fmdNoError

@gotEvent	lda eventHook+1
	beq @noEventHook	;Zero, no event massaging.

	pei event+2	;Give the event hook something
	pei event	;to play with.
	phk	;Push where we want to return to.
	pea @noEventHook-1

	pha	;Push the eventHook address and return
	phb	;to it.
	pla
	lda eventHook
	dec a
	pha
	rtl

@noEventHook	ldy #owhat
	lda [event],y
	sta code

	jsr doMouseDown
	bcc @exit

	jsr doMenuKey
	bcc @exit	;The key was a menu key.

	jsr doCutCopyPaste
	bcc @exit	;We did a cut, copy, or paste.

	jsr doActivate
	bcc @exit	;We did an activate.

	jsr doUpdate
	bcc @exit	;We did an update.

	pha
	lda #0
	pha
	pha
	pha
	pei event+2
	pei event
	_SendEventToCtl
	pla
	beq @exit0

	ldy #owmTaskData2	;Get the ctl hndl of affected ctl.
	lda [event],y
	sta ctlHndl	;Save this in ctlHndl.
	iny
	iny
	lda [event],y
	sta ctlHndl+2
	jsr setValues
	brl @exit

***

doCutCopyPaste	lda editTask	;We already have a task, 
	bne @atask	;so don't check keypress.

	lda code
	cmp #keyDownEvt
	beq @akey
	cmp #autoKeyEvt
	beq @akey

@secexit	sec
	rts

@akey	lda flags
	and #fmdMenuKey
	bne @secexit
	ldy #omodifiers	;See if it is a cut/copy/paste command.
	lda [event],y
	and #controlKey+optionKey+appleKey
	cmp #appleKey
	bne @secexit	;Wrong modifiers -- no go.

	ldy #omessage
	lda [event],y
	ora #$20	;Lower-case it.

	ldx #251
	cmp #'x'
	beq @yes
	inx
	cmp #'c'
	beq @yes
	inx
	cmp #'v'
	sec
	bne @secexit
@yes	stx editTask	;We have a valid task now.

@atask	pha	;Find out what control we are talking
	pha	;about.
	_FindTargetCtl
	ply
	sty ctlHndl
	ply
	sty ctlHndl+2
	bcs @secexit

	lda editTask	;251 thru 254 (cut/copy/paste/clear)
	stz editTask	;This is also a flag, so we need to set
	sec	;it back.
	sbc #251-4
	asl a	;Assume TextEdit.
	tax
	stz theHndl	
	stz theHndl+2

	jsr getCtlProc	;Find out what tool it is.
	cmp #editTextControl>>16
	beq @b	;It is TextEdit, and the xreg is set up
		;for TextEdit.

	cmp #editLineControl>>16
	bne @secexit	;Not a LineEdit tool, so there can be
		;no cut/copy/paste.

	ldy #octlData	;It is LineEdit.
	lda [ctlPtr],y
	sta theHndl
	iny
	iny
	lda [ctlPtr],y
	sta theHndl+2
	txa
	and #7
	tax	;LineEdit tool.

@b	pei theHndl+2	;All the below routines will use this
	pei theHndl	;parameter eventually.
	jmp (@theTask,x)

@theTask	dc.w @leCut, @leCopy, @lePaste, @leClear
	dc.w @teCut, @teCopy, @tePaste, @teClear

@leCut	_LECut	;Do the cut.
@leCut0	lda flags
	lsr a
	bcs @clcexit
	_ZeroScrap	;Then conditionally do the LEToScrap.
	pha	;Conditionally, because of the bug for
	_LEGetScrapLen	;zero-length scrap.
	pla
	beq @clcexit
	_LEToScrap
@clcexit	clc
	rts
@clcexitz	pla
	pla
	clc
	rts

@leCopy	_LECopy	;Do the copy.
	bra @leCut0

@lePaste	lda flags
	lsr a
	bcs @c

	pha	;Boy, are we good citizens, or what?
	pha
	pei zero
	_GetScrapSize
	plx
	ply
	bcs @clcexitz	;Give up and do nothing.
	tya
	bne @clcexitz	;Way too big, so give up.
	txa
	beq @clcexitz	;Too small, so give up.
	_LEFromScrap
	bcs @clcexitz	;Somebody wasn't pleased, so give up.
@c	_LEPaste
	clc
	rts

@leClear	_LEDelete	;Do the clear.
	clc
	rts

@teCut	ldy #$28+2	;Find out if we are read-only.
	lda [ctlPtr],y
	and #$0400	;Bit 26, please.
	beq @teCut0	;Not read-only, so it is okay.
	pla
	pla
	bra @teCut1
@teCut0	_TECut	;Do the cut.
@teCut1	clc
	rts

@teCopy	_TECopy	;Do the copy.
	clc
	rts

@tePaste	_TEPaste	;Do the paste.
	clc
	rts

@teClear	_TEClear	;Do the clear.
	clc
	rts

getCtlProc	ldy #2	;Find out what tool it is.
	lda [ctlHndl],y
	sta ctlPtr+2
	lda [ctlHndl]
	sta ctlPtr	;Handle now dereferenced.
	ldy #octlProc+2	;Get hi-word of proc address.
	lda [ctlPtr],y
	rts

***

doActivate	lda code
	cmp #activateEvt
	beq @a
	sec
	rts

@a	pha
	pha
	_GetPort

	pha	;Result space for _GetWControls.
	pha
	ldy #omessage+2	;Find out which window we are
	lda [event],y	;talking about.
	tax
	dey
	dey
	lda [event],y
	phx
	pha
	phx
	pha
	_SetPort

	ldy #omodifiers	;See if we are activating.
	lda [event],y
	lsr a
	bcc @noMenuProc	;We are not, so don't update menus.

	pha	;See if we have a proc to set the state
	pha	;of the menus.
	jsl fmdGetMenuProc
	plx	;Lo-word of address.
	pla	;Hi-word of address.
	bne @haveMenuProc	;We have a menu proc -- go do it.
	txy
	beq @noMenuProc	;We don't have one.

@haveMenuProc	phk	;Push where we want to return to.
	pea @noMenuProc-1
	xba	;Push just the lo-byte as the hi-byte
	pha	;of ret addr.
	phb
	pla
	dex	;Push the lo-word minus 1 for rtl.
	phx
	rtl

@noMenuProc	_GetWControls
	pla
	sta ctlHndl
	pla
	sta ctlHndl+2

@loop	lda ctlHndl+1
	beq @exit
	jsr getCtlProc

	ldx #@nakend-@nak-2
@naklook	cmp >@nak,x
	beq @nextCtl
	dex
	dex
	bpl @naklook

	cmp #editLineControl>>16
	bne @inval	;Invalidate unless lineEdit.
		;Invalidate this one selectively.  If
		;it is the window's target, then
		;invalidate it.

	ldy #octlMoreFlags
	lda [ctlPtr],y
	bpl @nextCtl	;It doesn't need invalidating.

@inval	lda ctlPtr	;Push pointer to bounding rect of ctl.
	clc
	adc #octlRect
	tax
	lda ctlPtr+2
	adc #0
	pha
	phx
	_InvalRect
	jsr getCtlProc	;An easy way to redereference.

@nextCtl	ldy #2
	lda [ctlPtr]
	sta ctlHndl
	lda [ctlPtr],y
	sta ctlHndl+2
	bra @loop

@exit	_SetPort
	clc	;displayed in the correct state.
	rts

@nak	dc.w $8100,$8900,$8D00 ;These don't need invalidating ever.
@nakend

***

doUpdate	lda code
	cmp #updateEvt
	beq @a
	sec
	rts	;Event isn't an update event.

@noUpdate	stz code	;We don't do this update, since the
	clc	;window that needs to be updated isn't
	rts	;the modal dialog.

@a	ldy #omessage+2	;Find out which window we are
	lda [event],y	;talking about.
	sta tempwptr+2
	tax
	dey
	dey
	lda [event],y
	sta tempwptr
	tay

	lda flags
	and #fmdUpdateAll
	bne @doUpdate	;We are movable/modal, so update it,
		;whatever it is.

	cpy dlgwptr
	bne @noUpdate	;We are modal, so don't update other
	cpx dlgwptr+2	;windows.
	bne @noUpdate	;We are modal, so don't update other
		;windows.

@doUpdate	phx	;Result space for _GetContentDraw.
	phy
	phx	;For _GetContentDraw
	phy
	_GetContentDraw
	plx
	pla
	bne @haveProc
	txy
	beq @exit

@haveProc	phk	;Push return address for updateProc.
	pea @retLoc-1

	xba	;Push just the lo-byte as the hi-byte
	pha	;of ret addr.
	phb
	pla

	dex	;Push the lo-word minus 1 for rtl.
	phx

	pei tempwptr+2
	pei tempwptr
	_BeginUpdate

	rtl	;Go to the updateProc.

@retLoc	pei tempwptr+2
	pei tempwptr
	_EndUpdate

@exit	clc
	rts

***

doMouseDown	lda code
	cmp #mouseDownEvt
	beq @a
	sec
	rts

@a	pha	;Find out what window we clicked on.
	pei zero
	tdc
	clc
	adc #whichWindow
	pha
	ldy #owhere+2
	lda [event],y
	sta xloc
	pha
	dey
	dey
	lda [event],y
	sta yloc
	pha
	_FindWindow
	pla
	bpl @aa

	tax	;Keep it for _SystemClick's use.
	lda flags
	and #fmdDeskAcc
	beq @toBeep

	pei event+2	;Let the DA have some fun, too.
	pei event
	pei whichWindow+2
	pei whichWindow
	phx
	_SystemClick
	clc
	rts

@aa	cmp #wInDrag
	bne @b

	jsr isMyWindow	;See if "whichWindow" equals dlgwptr.
	bcc @toBeep	;Wrong window -- nice try, though.

	lda flags
	bpl @az	;@noBeep

	ldy #0	;Drag resolution -- default.
	phy
	pei xloc	;Starting mouse location.
	pei yloc
	pea 8	;TaskMaster uses this value, so we do.
	phy	;Default cursor boundary.
	phy
	pei dlgwptr+2
	pei dlgwptr
	_DragWindow
	ldy #omodifiers
	lda [event],y
	and #appleKey
	bne @az
	pei dlgwptr+2
	pei dlgwptr
	_SelectWindow
@az	brl @noBeep

@b	cmp #wInMenuBar
	bne @c	;Not in menu bar.
	lda flags
	lsr a
	lsr a
	bcc @toBeep	;_MenuSelect not allowed -- go do beep.
	lda flags
	and #fmdMenuSelect+fmdMenuKey
	beq @toBeep	;Modal dialog, no menus allowed, so go
		;do beep.
	brl doMenuTask	;Movable/modal dialog, so go
		;handle menu bar.

@c	cmp #wInFrame	;Don't beep if on dialog frame.  This
	beq @d	;branch is the most efficient sizewise,
		;since we won't hit any controls.
	cmp #wInContent
	bne @toBeep	;We didn't click in a content.

@d	jsr isMyWindow
	bcs @e
@toBeep	brl @beep

@e	pha
	pha
	_FrontWindow
	pei dlgwptr+2
	pei dlgwptr
	_SelectWindow

	pla
	plx
	cmp whichWindow
	bne @noBeep
	cpx whichWindow+2
	bne @noBeep

@f	pha
	pei zero
	tdc
	clc
	adc #ctlHndl
	pha
	pei xloc
	pei yloc
	pei dlgwptr+2
	pei dlgwptr
	_FindControl
	pla
	beq @noBeep	;Missed all of the controls.	

	jsr fixLEflash

	pha
	pei xloc
	pei yloc
	lda #-1
	pha
	pha
	pei ctlHndl+2
	pei ctlHndl
	_TrackControl
	pla
	sta >ctlPart
	beq @noBeep

	ldy #owmTaskData2	;Put the found control in TaskData2
	lda ctlHndl	;for application.
	sta [event],y
	iny
	iny
	lda ctlHndl+2
	sta [event],y
	jsr setValues
	clc
	rts

@beep	lda beepProc+2
	bmi @noBeep	;Negative, do nothing.
	ora beepProc
	beq @sysBeep	;Zero, regular SysBeep.

	pei event+2	;Give the custom "beep" something
	pei event	;to play with.
	phk	;Push where we want to return to.
	pea @noBeep-1
	lda beepProc+1	;Push updateProc addr and return to it.
	pha
	phb
	pla
	lda beepProc
	dec a
	pha
	rtl
@sysBeep	_SysBeep
@noBeep	clc
	rts

fixLEflash	jsr getCtlProc
	cmp #editLineControl>>16
	bne @rts
	ldy #octlMoreFlags
	lda [ctlPtr],y
	bmi @rts	;LineEdit control already target.

	ldy #octlData	;For lineEdit controls that are
	lda [ctlPtr],y	;inactive, make sure that there is no
	sta theHndl	;text currently selected.  If there is,
	iny	;TrackControl will temporarily flash
	iny	;the selected range.  So, we check to
	lda [ctlPtr],y	;see if we are a lineEdit control.  If
	sta theHndl+2	;we are, we see if we are active.  If
	ldy #2	;we aren't, we set selEnd to be the
	lda [theHndl]	;same as selStart.  Simple, huh?
	sta thePtr
	lda [theHndl],y
	sta thePtr+2

	ldy #oleSelStart	;thePtr is deref'ed lineEdit record.
	lda [thePtr],y	;Set selEnd to be the same as selStart.
	iny
	iny
	sta [thePtr],y	;It is done.  It is good.  The end.

@rts	rts

***

fmdGetCtlPart
FMDGETCTLPART	lda >ctlPart
	sta 4,s
	jml _fmdNoError
ctlPart	dc.w 0

***

setValues	pha	;Convert the control handle into an ID
	pha	;for application.
	pei ctlHndl+2
	pei ctlHndl
	_GetCtlID
	pla
	ply
	bcs @a	;List controls have non-super-control
		;scrollbars.  We therefore don't want
		;to return a control-id here, since
		;non-super-controls don't have an id to
		;return anyway.  (We get an error $1007
		;if it isn't a super-control, so we
		;don't want to return anything
		;anything, since it garbage.)
	sta retval
	sty retval+2	;Return the ctlID in retval.

@a	jsr getCtlProc	;See if radio button or checkbox.
	cmp #radioControl>>16
	beq @radioButtonHit
	cmp #checkControl>>16
	bne @rts

@checkBoxHit	pha
	pei ctlHndl+2
	pei ctlHndl
	_GetCtlValue
	pla
	beq @c	;Do a true NOT.
	lda #$FFFF
@c	inc a
@d	pha
	pei ctlHndl+2
	pei ctlHndl
	_SetCtlValue
@rts	rts

@radioButtonHit	lda #1
	bra @d

doMenuTask	clc
doMenuTaskz	php	;Keep carry status.
	pei event+2
	pei event
	lda #0
	pha
	pha
	bcs @b
	jsr ibeamTest
	bcc @a
	_InitCursor	;This only gets done if it is currently
		;an ibeam.
@a	_MenuSelect	;Handle the pullDown.
	bra @c
@b	_MenuKey	;Handle the menu key.

@c	plp	;Restore carry status.
	ldy #owmTaskData
	lda [event],y
	beq @rts	;PullDown, cclear.  MenuKey, cset.
	tax
	iny
	iny
	lda [event],y
	sta menunum

	cpx #250	;See if we should open a DA.
	bcs @d

	lda flags	;See if we should handle DA's.
	and #fmdDeskAcc
	beq @y	;No, so hilite menu to normal.
	phx	;Open the can-o-worms.
	phx
	_InitCursor
	_OpenNDA
	pla
	bra @y	;Do _HiliteMenu and leave.

@d	cpx #256	;Anything above close, let app do it.
	bcs @z	;Not an undo/cut/copy/paste/clear/close
		;menu item.

	ldy wkind
	bpl @e

	cpx #255
	beq @closeNDA	;Go let the DA handle it.

	txa
	sec
	sbc #249
	pha
	pha
	_SystemEdit
	pla
	bra @y	;Do _HiliteMenu and leave.

@e	cpx #255
	beq @z	;Let app do close for non DA windows.
	cpx #250
	beq @z	;Let app do undo also.

	stx editTask	;Let cut/copy/paste know what to do.
	jsr doCutCopyPaste

@y	pei zero
	pei menunum
	_HiliteMenu
	clc
	rts

@z	stx retval	;Low-order word contains the ID # of
	ora #$8000	;item selected.  Hi-order word contains
	sta retval+2	;the menu ID # with hi-bit turned on.
	clc	;The hi-bit is to allow the application
		;to distinguish this from a control ID.
		;This means ID's must be hi-bit off!!
@rts	rts

@closeNDA	pha
	pha
	_FrontWindow
	lda 1,s	;Be extra paranoid -- it helps.
	cmp keepPort	;Make sure that the desk accessory was
	bne @db	;not active port when fakeModalDialog
	lda 3,s	;was called.  If it is, we will get
	cmp keepPort+2	;into trouble closing it, because we
	bne @db	;will do a SetPort to keepPort when we
	lda dlgwptr	;leave.  So, if keepPort is the port we
	sta keepPort	;are about to close, change it to the
	lda dlgwptr+2	;dlgwptr (the app's dialog window).
	sta keepPort+2
@db	_CloseNDAByWinPtr
	bra @y

isMyWindow	lda whichWindow
	cmp dlgwptr
	bne @no
	lda whichWindow+2
	cmp dlgwptr+2
	beq @yes
@no	clc
@yes	rts

***

doMenuKey	lda code
	cmp #keyDownEvt
	beq @a
	sec
@rts	rts	;It isn't a menu key, since it isn't
		;even a key.

@a	lda flags
	and #fmdMenuKey
	sec
	beq @rts	;Menu keys not allowed (carry is set).

	brl doMenuTaskz	;Carry still set, which is important.

***

ibeamTest	lda wkind
	clc
	bmi @rts	;Don't do our ibeam when a DA is up.
	pha	;This is so we know what cursor we
	pha	;currently have.  Otherwise, we will be
	_GetCursorAdr	;setting the cursor a lot, and it will
	pha	;flash.
	pha
	jsl fmdGetIBeamAdr	;This clears carry (no error returned.)
	pla
	ply
	eor 1,s
	bne @no	;Carry is clear.
	tya
	eor 3,s
	bne @no	;Carry is clear.
	sec
@no	pla
	pla
@rts	rts	;Carry set means we now have ibeam.

	ENDP


*******************************************************************************
*******************************************************************************
*******************************************************************************

* From this point are additional entry points and useful access routines.

********************

	export fmdStdDrawProc
fmdStdDrawProc	PROC
	export FMDSTDDRAWPROC

FMDSTDDRAWPROC	pha	;What kind of window frame do we want?
	pha
	pha
	_GetPort
	_GetWFrame
	pla	;fAlert is bit 13.  fFlex is bit 9.
	asl a
	asl a	;fAlert is now in bit 15.
	bmi @noFrame	;We already have a frame, and
		;it looks maaahvelous!

	and #$0800	;Bit 9 moved into bit 11.
	bne @noFrame

	pha
	_GetMasterSCB
	pla
	xba
	asl a
	lda #5
	bcs @in640
	lsr a
@in640	pha	;Keep the width for _InsetRect.
	pha	;Push the width for _FrameRect.
	pea 2
	_SetPenSize
	plx	;The width for _InsetRect.
	lda #@workRect
	ldy #@workRect>>16
	phy	;Push parameters for _FrameRect.
	pha
	phy	;Push parameters for _InsetRect.
	pha
	phx	;Push the width for _InsetRect.
	pea 2
	phy	;Push parameters for _GetPortRect.
	pha
	_GetPortRect
	_InsetRect
	_FrameRect
	_PenNormal

@noFrame	pha
	pha
	_GetPort
	_DrawControls
	rtl

@workRect	ds.w 4

	ENDP

********************

	export fmdSetMenuProc
fmdSetMenuProc	PROC
	export FMDSETMENUPROC
	export fmdGetMenuProc, FMDGETMENUPROC
	import _fmdNoError

FMDSETMENUPROC	lda 4,s
	sta >menuProc
	lda 6,s
	sta >menuProc+2
	lda 1,s
	sta 5,s
	lda 2,s
	sta 6,s
	pla
	pla
exit	jml _fmdNoError

fmdGetMenuProc
FMDGETMENUPROC	lda >menuProc
	sta 4,s
	lda >menuProc+2
	sta 6,s
	bra exit

menuProc	dc.l 0

	ENDP

********************

	export fmdEditMenu
fmdEditMenu	PROC
	export FMDEDITMENU
	import _fmdNoError

	DefineStack

selStart	long
selEnd	long
leHndl	long
lePtr	long
ctlPtr	long	;Must be 2nd from end of local space.
ctlHndl	long	;Must be at end of local space.

sizeLocals	EndLocals

saveDPage	word
returnAddr	block 3

***

FMDEDITMENU	phd	;Save directPage register.

	tsc	;Make space for locals, part 1.
	sec
	sbc #sizeLocals
	tcd

	pha	;Find out if it is a DA window.
	pha
	pha
	_FrontWindow
	_GetWKind
	pla
	bpl @a	;It is a regular window.
	pea 250
	_EnableMItem	;Enable undo.
	pea 255
	_EnableMItem	;Enable close.
	ldx #%1111	;Enable cut/copy/paste/clear.
	brl @setMenus

@a	pha
	pha
	_FindTargetCtl
	bcc @aa	;There is a target, so go do some work.
	brl @noTool

@aa	ldy #2	;Deref target ctl handle into ctlPtr.
	lda [ctlHndl],y
	pha
	lda [ctlHndl]
	pha

	tdc	;Make space for locals, part 2.
	tcs	;Stack ptr & directPage ptr agree now.

	ldy #octlProc+2	;Get hi-word of proc address.
	lda [ctlPtr],y


	cmp #editTextControl>>16
	beq @textEdit	;It is TextEdit tool.
	cmp #editLineControl>>16
	bne @noTool	;Not LineEdit tool.

@lineEdit	ldy #octlData	;Get the lineEdit handle from
	lda [ctlPtr],y	;the control's data field.
	sta leHndl
	iny
	iny
	lda [ctlPtr],y
	sta leHndl+2

	ldy #2	;Dereference leHndl.
	lda [leHndl]
	sta lePtr
	lda [leHndl],y
	sta lePtr+2

	ldx #0
	jsr canWePaste

	ldy #oleSelStart
	lda [lePtr],y
	iny
	iny
	cmp [lePtr],y
	beq @setMenus
	txa
	ora #%1011	;Enable cut/copy/clear.
	tax	
	bra @setMenus

@textEdit	ldy #0
	phy
	tdc
	clc
	adc #selStart
	pha
	phy
	adc #selEnd-selStart
	pha
	pei ctlHndl+2
	pei ctlHndl
	_TEGetSelection
	ldx #%1011
	lda selStart
	cmp selEnd
	bne @hasSelect
	lda selStart+2
	cmp selEnd+2
	bne @hasSelect
	ldx #0

@hasSelect	jsr canWePaste

	ldy #$28+2	;Find out if we are read-only.
	lda [ctlPtr],y
	and #$0400	;Bit 26, please.
	beq @setMenus

	txa	;Don't allow cut & paste.
	and #2
	tax
	bra @setMenus

@noTool	ldx #0	;Bit 0 for cut, 1 for copy,
		;2 for paste.
@setMenus	txa
	ldy #251
	jsr setOneMenu
	jsr setOneMenu
	jsr setOneMenu
	jsr setOneMenu

@exit	tdc	;Remove local space.
	clc
	adc #sizeLocals
	tcs
	pld	;Restore directPage register.
	jml _fmdNoError

setOneMenu	lsr a
	pha
	phy
	phy
	bcs @enable
@disable	_DisableMItem
	bra @a
@enable	_EnableMItem
@a	ply
	iny
	pla
	rts

canWePaste	txa	;Assume no paste.
	and #$FFFF-4
	pha

	pha	;See if paste should be available.
	pha
	pea 0
	_GetScrapSize
	plx
	ply

	pla	;Get cut/copy/paste status back.

	bcs @exit	;Error, so no paste.
	phy
	ply
	bne @yes
	txy
	beq @exit
@yes	ora #4

@exit	tax
	rts

	ENDP

********************

	export fmdFindCursorCtl
fmdFindCursorCtl	PROC
	export FMDFINDCURSORCTL
	import _fmdNoError

	DefineStack

hndl	long	;Must be at 1,s
ctlHndl	long
ctlPtr	long

sizeLocals	EndLocals

saveDPage	word
returnAddr	block 3

	BegParms
wptr	long
yloc	word
xloc	word
ctlHndlPtr	long	;Where to store the control handle.
sizeParms	EndParms

ctlPart	word

***

FMDFINDCURSORCTL	phd	;Save directPage register.
	tsc	;Make space for locals.
	sec
	sbc #sizeLocals
	tcs
	tcd	;Set directPage register.

	pei wptr+2
	pei wptr
	_GetWControls	;Result space already there (hndl).

	stz ctlPart	;Assume failure.
	stz ctlHndl
	stz ctlHndl+2

@loop	lda hndl+1
	beq @exit	;No more controls to check.

	ldy #2	;Deref hndl.
	lda [hndl]
	sta ctlPtr
	lda [hndl],y
	sta ctlPtr+2

	pha	;Result space for _PtInRect
	pea 0	;Push pointer to point.
	tdc
	clc
	adc #yloc
	pha
	lda ctlPtr	;Push ptr to bounding rect of ctl.
	clc
	adc #octlRect
	tax
	lda ctlPtr+2
	adc #0
	pha
	phx
	_PtInRect
	pla
	beq @nextCtl

	sta ctlPart
	lda hndl	;Copy the temp hndl to the "real" one.
	sta ctlHndl
	lda hndl+2
	sta ctlHndl+2

	ldy #octlProc+2	;Get hi-word of proc address.
	lda [ctlPtr],y
	cmp #editTextControl>>16
	bne @exit

@nextCtl	ldy #2
	lda [ctlPtr]	;Get the next control handle.
	sta hndl
	lda [ctlPtr],y
	sta hndl+2
	bra @loop

@exit	lda ctlHndl	;Return the ctl hndl (could be NULL).
	sta [ctlHndlPtr]
	lda ctlHndl+2
	ldy #2
	sta [ctlHndlPtr],y

	tsc	;Get rid of local variables.
	clc
	adc #sizeLocals
	tcs
	pld	;Restore directPage register.
	lda 1,s	;Move return address.
	sta 1+sizeParms,s
	lda 2,s
	sta 2+sizeParms,s
	tsc	;Get rid of passed parameters.
	adc #sizeParms
	tcs
	jml _fmdNoError

	ENDP

********************

* Here are some useful routines for control information access via a ctl ID.

********************

* This routine takes a window pointer, a lineEdit control ID, and a pointer to
* a pascal string.  It stuffs the pascal string into the lineEdit control.
* It also select the full range of the text.  This is useful because the
* target control should have all the text selected when a dialog comes up.
* Doing it here means you don't have to do it elsewhere.

	export fmdLESetText
fmdLESetText	PROC
	export FMDLESETTEXT
	export fmdLEGetText, FMDLEGETTEXT
	import _fmdSetError

	DefineStack

ctlHndl	long
ctlPtr	long
leHndl	long
lePtr	long
lineHndl	long
linePtr	long
cstr	long
lineLength	word
rect	block 8	;Must be > oleViewRect ($10)

sizeLocals	EndLocals

saveDPage	word
returnAddr	block 3

	BegParms
pstr	long	;Pointer to string space.
lineEditID	long	;ID of lineEdit control.
wptr	long	;Window that owns the control.
sizeParms	EndParms

***

FMDLESETTEXT	jsr lineEditSetup	;Set up everything we need.
	bcs exit	;Couldn't dereference for some reason.

	pei cstr+2	;Set text to c-string.
	pei cstr
	lda [pstr]	;Get length of pascal string.
	and #$FF
	pha
	pei leHndl+2
	pei leHndl
	_LESetText
	bcs exit

	pha	;Invalidate view rect of lineEdit.
	pha
	_GetPort	;Save the current port.
	pei wptr+2
	pei wptr
	_SetPort	;Make the window the current port
		;(for _InvalRect).

	jsr lineEditDeref	;Redereference -- slow but small.
	ldy #oleSelStart
	lda #0
	pha	;Hi-word for _InvalRect.
	sta [lePtr],y
	iny
	iny	;Point at leSelEnd.
	lda [pstr]	;Get length of pascal string.
	and #$FF
	sta [lePtr],y

	ldy #oleViewRect+6	;Copy lineEdit viewRect into rect.
@a	lda [lePtr],y
	tyx
	sta <rect-oleViewRect,x
	dey
	dey
	cpy #oleViewRect
	bcs @a

	tdc	;Hi-word alreay on stack.  (It's a 0).
	adc #rect	;Carry still clear.
	pha
	_InvalRect	;Inval that rect.

	_SetPort	;Put the port back.

exitNoErr	lda #0	;Return no error.
exit	tay	;Save error code.

	tdc	;Get rid of local variables.
	clc
	adc #sizeLocals
	tcs
	pld	;Restore directPage register.

	lda 1,s	;Move return address.
	sta 1+sizeParms,s
	lda 2,s
	sta 2+sizeParms,s

	tsc	;Pull passed parms off stack.
	adc #sizeParms
	tcs

	tya	;Recover error code.
	jml _fmdSetError

***

fmdLEGetText
FMDLEGETTEXT	jsr lineEditSetup	;Set up everything we need.
	bcs exit	;Couldn't dereference for some reason.

	pei linePtr+2	;Copy the text into the string.
	pei linePtr
	pei cstr+2
	pei cstr
	pea 0
	pei lineLength
	_BlockMove

	ldy lineLength	;Store the p-string length and also
	tya	;terminate the string for the c dudes.
	shortm	;Use 8-bit accumulator.
	sta [pstr]	;Save the p-string length byte.
	lda #0
	sta [cstr],y	;Terminate the c-string.
	longm	;Set accumulator back to 16-bit.

	bra exitNoErr	;Return no error.

***

lineEditSetup	plx	;Save return address so stack frame
		;setup works.

	phd	;Save directPage register.
	tsc	;Make space for locals.
	sec
	sbc #sizeLocals
	tcs
	tcd	;Set directPage register.

	phx	;Put return address back.

	lda pstr	;Also point past p-string length
	ldx pstr+2	;for c-strings.
	inc a
	bne @a
	inx
@a	sta cstr
	stx cstr+2

	pha	;Get the control handle via wptr,id.
	pha
	pei wptr+2
	pei wptr
	pei lineEditID+2
	pei lineEditID
	_GetCtlHandleFromID
	plx
	stx ctlHndl
	plx
	stx ctlHndl+2
	bcs rts1

	ldy #2	;Dereference ctlHndl.
	lda [ctlHndl]
	sta ctlPtr
	lda [ctlHndl],y
	sta ctlPtr+2

	ldy #octlData	;Get the lineEdit handle from
	lda [ctlPtr],y	;the control's data field.
	sta leHndl
	iny
	iny
	lda [ctlPtr],y
	sta leHndl+2

lineEditDeref	ldy #2	;Dereference leHndl.
	lda [leHndl]
	sta lePtr
	lda [leHndl],y
	sta lePtr+2

	ldy #oleLength
	lda [lePtr],y
	sta lineLength

	ldy #oleLineHandle
	lda [lePtr],y
	sta lineHndl
	iny
	iny
	lda [lePtr],y
	sta lineHndl+2

	ldy #2	;Dereference lineHndl.
	lda [lineHndl]
	sta linePtr
	lda [lineHndl],y
	sta linePtr+2
	clc	;Everything worked.

rts1	rts

	ENDP

********************

	export fmdWhichRadio
fmdWhichRadio	PROC
	export FMDWHICHRADIO
	import _fmdSetError

	DefineStack

ctlHndl	long	;Must be at 1,s
ctlPtr	long
ctlID	long	;ID of some radio button control.
theRadBut	word
notHere	word	;Flag for active button not found.

sizeLocals	EndLocals

saveDPage	word
returnAddr	block 3

	BegParms
famNum	word
wptr	long	;Window that owns the control.
sizeParms	EndParms

radioNum	word

***

FMDWHICHRADIO	phd	;Save directPage register.
	tsc	;Make space for locals.
	sec
	sbc #sizeLocals
	tcs
	tcd	;Set directPage register.

	lda #$FFFF	;This is supposed to be a bogus value
	sta radioNum	;(0 is legit).
	sta notHere	;Assume we won't find active radio
	sta ctlID+2	;button of correct family number.

	pei wptr+2
	pei wptr
	_GetWControls	;Result space already there (ctlHndl).
	bcs @exit

@loop	lda ctlHndl+1	;See if we have a NULL handle yet.
	beq @endLoop

	jsr @derefCtl	;Deref ctlHndl into ctlPtr.

	ldy #octlProc+2	;Get hi-word of proc address.
	lda [ctlPtr],y
	cmp #radioControl>>16
	bne @nextCtl	;Not a radio button.  Skip it.

	ldy #octlFlag
	lda [ctlPtr],y
	and #$7F
	cmp famNum	;See if it is "our" family.
	bne @nextCtl	;Not related.

	ldy #octlID+2	;See if this is the smallest ctlID for
	lda [ctlPtr],y	;our family yet.
	tax
	dey
	dey
	lda [ctlPtr],y
	cpx ctlID+2
	bcc @smaller
	bne @a	;It isn't.
	cmp ctlID
	bcs @a	;It isn't.
@smaller	sta ctlID	;It is, so remember it.
	stx ctlID+2

@a	tax
	ldy #octlValue
	lda [ctlPtr],y
	beq @nextCtl	;This isn't the active radio button.
	stx theRadBut	;This is the one, so remember enough
	stz notHere	;of it.  Also, flag we found it.

@nextCtl	ldy #2
	lda [ctlPtr]
	sta ctlHndl
	lda [ctlPtr],y
	sta ctlHndl+2
	bra @loop

@endLoop	lda notHere	;See if we found it.  If we didn't,
	bmi @b	;return $FFFF to indicate this.
	lda theRadBut
	sec
	sbc ctlID
@b	sta radioNum	;This is what the programmer was
		;yearning for.

	lda #0	;Return no error.

@exit	tay	;Save error code.

	tdc	;Get rid of local variables.
	clc
	adc #sizeLocals
	tcs
	pld	;Restore directPage register.

	lda 1,s	;Move return address.
	sta 1+sizeParms,s
	lda 2,s
	sta 2+sizeParms,s

	tsc	;Pull passed parms off stack.
	adc #sizeParms
	tcs

	tya	;Recover error code.
	jml _fmdSetError

@derefCtl	ldy #2
	lda [ctlHndl]
	sta ctlPtr
	lda [ctlHndl],y
	sta ctlPtr+2
	rts

	ENDP

********************

	export fmdIBeamCursor
fmdIBeamCursor	PROC
	export FMDIBEAMCURSOR
	export fmdInitIBeam, fmdGetIBeamAdr, fmdSetIBeam
	export FMDINITIBEAM, FMDGETIBEAMADR, FMDSETIBEAM
	import _fmdNoError

FMDIBEAMCURSOR	lda >curIBeam+2	;Change the cursor to current ibeam.
	pha
	lda >curIBeam
	pha
	_SetCursor
	bra exit

fmdInitIBeam
FMDINITIBEAM	lda #ibeamCursor	;Make default ibeam current ibeam.
	sta >curIBeam
	lda #ibeamCursor>>16
	sta >curIBeam+2
	bra exit

fmdGetIBeamAdr
FMDGETIBEAMADR	lda >curIBeam	;Return address of current ibeam.
	sta 4,s
	lda >curIBeam+2
	sta 6,s
	bra exit

fmdSetIBeam
FMDSETIBEAM	lda 4,s	;Change the ibeam to alternate ibeam.
	sta >curIBeam
	lda 6,s
	sta >curIBeam+2
	lda 1,s
	sta 5,s
	lda 2,s
	sta 6,s
	pla
	pla
exit	jml _fmdNoError

curIBeam	dc.l 0
ibeamCursor	dc.b 9,0,3,0
	dc.b $00,$F0,$F0,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$0F,$00,$00,$00,$00
	dc.b $00,$F0,$F0,$00,$00,$00
		
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b $00,$00,$00,$00,$00,$00
	dc.b 4,0,6,0

	ENDP

********************

	export fmdStartUp
fmdStartUp	PROC
	export FMDSTARTUP

FMDSTARTUP	lda #0
	pha
	pha
	jsl fmdSetMenuProc
	jml fmdInitIBeam

	ENDP

********************

	export fmdShutDown
fmdShutDown	PROC
	export FMDSHUTDOWN
	import _fmdNoError

FMDSHUTDOWN	jml _fmdNoError

	ENDP

********************

	export _fmdNoError
_fmdNoError	PROC
	export _fmdSetError, fmdGetError

	lda #0

_fmdSetError	sta >fmdErr
	bra exit

fmdGetError	lda >fmdErr
	sta 4,s
exit	cmp #1
	rtl

fmdErr	dc.w 0

	ENDP

********************

	END