[comp.sources.amiga] v90i069: Snap 1.4 - cut, store, and paste between windows, Part03/04

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (02/12/90)

Submitted-by: micke@slaka.sirius.se (Mikael Karlsson)
Posting-number: Volume 90, Issue 069
Archive-name: util/snap-1.4/part03

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 4)."
# Contents:  source/handler.s source/minrexx.c source/snapchars.c
# Wrapped by tadguy@xanth on Sun Feb 11 17:48:46 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'source/handler.s' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'source/handler.s'\"
else
echo shar: Extracting \"'source/handler.s'\" \(17796 characters\)
sed "s/^X//" >'source/handler.s' <<'END_OF_FILE'
X        include "exec/types.i"
X        include "exec/lists.i"
X        include "devices/inputevent.i"
X
Xwaiting   equ 0
Xselregion equ 1
Xwaitext   equ 2
Xselgfx    equ 3
Xwaitgfx   equ 4
Xinserting equ 5
Xpendsnap  equ 6
Xkillbutt  equ 7
X
Xnoaction  equ 0
Xsnapgfx   equ 1
Xsnaptext  equ 2
Xsnapinit  equ 3
Xinsert    equ 4
Xcancel    equ 5
X
XLMB       equ IECODE_LBUTTON
XLMB_UP    equ IECODE_UP_PREFIX+IECODE_LBUTTON
XRMB       equ IECODE_RBUTTON
XRMB_UP    equ IECODE_UP_PREFIX+IECODE_RBUTTON
XNO_BUTT   equ IECODE_NOBUTTON
XLCOM      equ IEQUALIFIER_LCOMMAND
XSHIFT     equ IEQUALIFIER_LSHIFT+IEQUALIFIER_RSHIFT
X
X
X        XDEF    _myhandler
X        XREF    _geta4
X        XREF    _LVOSignal
X        XREF    _SysBase
X        XREF    _MyTask
X        XREF    _action
X        XREF    _state
X        XREF    _modinsert
X        XREF    _startsignal
X        XREF    _insertsignal
X        XREF    _cancelsignal
X        XREF    _donesignal
X        XREF    _movesignal
X        XREF    _clicksignal
X        XREF    _timersignal
X        XREF    _initsignal
X        XREF    _cwsignal
X        XREF    _textqual                      ; qualifier for snapping text
X        XREF    _gfxqual                       ;    -"-    -"-   -"-    gfx
X        XREF    _insertkey
X        XREF    _cwkey
X
X        SECTION CODE
X
X_myhandler:
X
X; On entry: a0 : Pointer to event list
X;           a1 : Pointer to data
X
X; In loop:  a1 : Pointer to event
X;           d0 : scratch
X
X; Result:   d0 : New event list
X
X        movem.l a4,-(sp)
X
X        jsr     _geta4                         ; Get offset base a4
X
X        move.l  a0,a1                          ; a1 = Event list = a0
X
X.nextevent
X        cmpa.l  #0,a1                          ; Check for end of list
X        beq     .done
X
X        cmp.w   #noaction,_action              ; forced noaction - cancel
X        bne     .notcanceled                   ; no, we're in action
X        move.w  #waiting,_state                ; no action -> wait state
X        bra     .dostate
X.notcanceled
X        cmp.w   #insert,_action                ; forced insert - cancel
X        bne     .dostate                       ; no
X        move.w  #inserting,_state              ; set correct state
X
X.dostate
X        move.w  _state,d0
X        asl.w   #1,d0
X        move.w  JumpTable(pc,d0.w),d0
XOrigin
X        jmp     0(pc,d0.w)
XJumpTable
X        dc.w    .Waiting-Origin-2
X        dc.w    .SelRegion-Origin-2
X        dc.w    .WaitExt-Origin-2
X        dc.w    .SelGfx-Origin-2
X        dc.w    .WaitGfx-Origin-2
X        dc.w    .Insert-Origin-2
X        dc.w    .PendSnap-Origin-2
X        dc.w    .CancelTxt-Origin-2
X
X; ********************************************************
X; State: Waiting    ~TQ
X; Actions     New state             Signal
X; TQ          PendSnap              init
X; LMB+GQ      SelGfx                init+start
X; LCOM+IKEY   Inserting             insert
X
X.Waiting
X        cmp.b   #IECLASS_RAWKEY,ie_Class(a1)   ; Is it RAWKEY?
X        bne     .wait_RAWMOUSE
X
X        move.w  ie_Qualifier(a1),d0
X        and.w   _textqual,d0                   ; TQ?
X        bne     .signalinit                    ; Yes -- init
X
X        move.w  ie_Qualifier(a1),d0
X        and.w   #LCOM,d0                       ; LCOM?
X        beq     .EventHandled                  ; No
X
X        move.w  _cwkey,d0                      ; Control window key
X        cmp.w   ie_Code(a1),d0
X        beq     .signalcw
X
X        move.w  _insertkey,d0
X        beq     .EventHandled                  ; Key = 0 -- disabled
X
X        cmp.w   ie_Code(a1),d0                 ; The insert key?
X        bne     .EventHandled                  ; No, pass it on
X        bra     .signalinsert                  ; Tell'em to insert
X
X.wait_RAWMOUSE
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        move.w  ie_Qualifier(a1),d0
X        and.w   _textqual,d0                   ; TQ?
X        bne     .signalinit                    ; Yes
X
X        cmp.w   #LMB,ie_Code(a1)               ; OK, is it SELECTDOWN?
X        bne     .EventHandled                  ; Too bad
X
X        move.w  ie_Qualifier(a1),d0
X        and.w   _gfxqual,d0                    ; GQ?
X        beq     .EventHandled                  ; No, not interested
X
X; Handle event LMB+GQ
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  #snapgfx,_action               ; action snapgfx;
X        move.w  #selgfx,_state                 ; state selgfx
X        move.l  _initsignal,d0                 ; Signal init and
X        or.l    _startsignal,d0                ; start
X        bra     SignalTask
X
X.signalinit
X        move.w  #pendsnap,_state
X        move.w  #snapinit,_action
X        move.l  _initsignal,d0                 ; Signal start
X        bsr     Signal
X        bra     .PendSnap
X
X
X; *******************************************************
X; State: PendSnap   TQ
X; Actions     New state           Signal
X; ~TQ         Waiting             cancel
X; LCOM+IKEY   Inserting           insert
X; LMB         SelRegion           start
X; RMB         Inserting           insert
X
X.PendSnap
X        cmp.b   #IECLASS_RAWKEY,ie_Class(a1)   ; RAWKEY?
X        bne     .ps_RAWMOUSE
X        move.w  ie_Qualifier(a1),d0
X        and.w   _textqual,d0                   ; TQ?
X        bne     .ps_IKEY                       ; Still down, continue
X
X;Handle event ~TQ
X.ps_TQ
X        move.w  #noaction,_action              ; no action
X        move.w  #waiting,_state                ; state waiting
X        move.l  _cancelsignal,d0               ; snap cancelled
X        bra     SignalTask
X
X.ps_IKEY
X        move.w  ie_Qualifier(a1),d0
X        and.w   #LCOM,d0                       ; LCOM?
X        beq     .EventHandled                  ; No
X
X        move.w  _cwkey,d0                      ; Control window key
X        cmp.w   ie_Code(a1),d0
X        beq     .signalcw
X
X        move.w  _insertkey,d0
X        beq     .EventHandled                  ; Key = 0 -- disabled
X
X        cmp.w   ie_Code(a1),d0                 ; The insert key?
X        bne     .EventHandled                  ; No, pass it on
X        bra     .signalinsert                  ; Tell'em to insert
X
X.ps_RAWMOUSE
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        move.w  ie_Qualifier(a1),d0            ; Might be TQ going up.
X        and.w   _textqual,d0                   ; TQ?
X        beq     .ps_TQ
X
X        cmp.w   #LMB,ie_Code(a1)               ; OK, is it SELECTDOWN?
X        bne     .ps_RMB                        ; Too bad
X
X; Handle event LMB+TQ
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  #snaptext,_action              ; action snaptext
X        move.w  #selregion,_state              ; state selregion
X        move.l  _startsignal,d0                ; start
X        bra     SignalTask
X
X.ps_RMB
X        cmp.w   #RMB,ie_Code(a1)               ; MENUDOWN
X        bne     .EventHandled
X
X; Handle event RMB+TQ
X.signalinsert
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X
X        move.w  #0,_modinsert
X        move.w  ie_Qualifier(a1),d0
X        and.w   #SHIFT,d0                      ; SHIFT?
X        beq     .NonModified                   ; No
X        move.w  #1,_modinsert
X.NonModified
X        move.w  #insert,_action
X        move.w  #inserting,_state
X        move.l  _insertsignal,d0               ; and insert
X        bra     SignalTask
X
X.signalcw
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  #noaction,_action
X        move.w  #waiting,_state
X        move.l  _cancelsignal,d0               ; Exit pending snap
X        or.l    _cwsignal,d0                   ; and tell'em to open window
X        bra     SignalTask
X
X
X; *******************************************************
X; State: SelRegion  LMB+TQ
X; Actions     New state           Signal
X; ~TQ         Waiting             cancel
X; ~LMB        WaitExt
X; MOVE        SelRegion           move
X; RMB         SelRegion           click
X; ~RMB        SelRegion                      Needs no action
X; TIMER       SelRegion           timer
X
X.SelRegion
X        cmp.b   #IECLASS_TIMER,ie_Class(a1)    ; Timer event?
X        bne     .sr_RAWKEY
X
X;Handle timer event
X        move.l  _timersignal,d0
X        bra     SignalTask
X
X.sr_RAWKEY
X        cmp.b   #IECLASS_RAWKEY,ie_Class(a1)   ; RAWKEY?
X        bne     .sr_LMB_UP
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  ie_Qualifier(a1),d0
X        and.w   _textqual,d0                   ; TQ?
X        bne     .EventHandled                  ; Still down, continue
X
X;Handle event ~TQ
X.sr_TQ
X        move.w  #cancel,_action                ; no action
X        move.w  #killbutt,_state              ; state waiting
X        move.l  _cancelsignal,d0               ; snap cancelled
X        bra     SignalTask
X
X.sr_LMB_UP
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        move.w  ie_Qualifier(a1),d0            ; Might be TQ going up.
X        and.w   _textqual,d0                   ; TQ?
X        beq     .sr_TQ
X
X        cmp.w   #LMB_UP,ie_Code(a1)            ; SELECTUP
X        bne     .sr_MOVE                       ; no, check for move
X
X; Handle event ~LMB
X        move.w  #waitext,_state                ; state waitext
X        bra     KillEvent
X
X.sr_MOVE
X        cmp.w   #NO_BUTT,ie_Code(a1)           ; MOVE
X        bne     .sr_RMB                        ; No, check for RMB
X        move.w  ie_Qualifier(a1),d0
X        and.w   #IEQUALIFIER_RELATIVEMOUSE,d0  ; RELATIVEMOUSE
X        beq     .sr_RMB
X
X; Handle event MOVE
X        move.l  _movesignal,d0
X        bra     SignalTask
X
X.sr_RMB
X        cmp.w   #RMB,ie_Code(a1)               ; MENUDOWN
X        bne     .EventHandled                  ; No, not interested
X
X; Handle event RMB
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.l  _clicksignal,d0
X        bra     SignalTask
X
X; ***************************************************
X; State WaitExt     TQ
X; Actions     New state           Signal
X; ~TQ         Waiting             done
X; LMB         SelRegion           click
X; MOVE        WaitExt                            No action needed.
X; RMB         Inserting           done & insert
X; TIMER       WaitExt             timer
X
X.WaitExt
X        cmp.b   #IECLASS_TIMER,ie_Class(a1)    ; Timer event?
X        bne     .we_RAWKEY
X
X;Handle timer event
X        move.l  _timersignal,d0
X        bra     SignalTask
X
X.we_RAWKEY
X        cmp.b   #IECLASS_RAWKEY,ie_Class(a1)   ; RAWKEY?
X        bne     .we_LMB
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  ie_Qualifier(a1),d0
X        and.w   _textqual,d0                   ; TQ?
X        bne     .EventHandled                  ; Still down, continue
X
X;Handle event ~TQ
X.we_TQ
X        move.w  #noaction,_action              ; no action
X        move.w  #waiting,_state                ; state waiting
X        move.l  _donesignal,d0                 ; snap finished
X        bra     SignalTask
X
X.we_LMB
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        move.w  ie_Qualifier(a1),d0            ; Might be TQ going up.
X        and.w   _textqual,d0                   ; TQ?
X        beq     .we_TQ                         ; No.
X
X        cmp.w   #LMB,ie_Code(a1)               ; SELECTDOWN
X        bne     .we_RMB                        ; no, check for RMB
X
X; Handle event LMB
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  #selregion,_state              ; state waitext
X        move.l  _clicksignal,d0                ; Extend selection
X        bra     SignalTask
X
X.we_RMB
X        cmp.w   #RMB,ie_Code(a1)               ; MENUDOWN
X        bne     .EventHandled                  ; No, not interested
X
X; Handle event RMB
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  #insert,_action
X        move.w  #inserting,_state
X        move.l  _donesignal,d0
X        or.l    _insertsignal,d0
X        bra     SignalTask
X
X; *******************************************************
X; State: SelGfx     LMB+GQ
X; Actions     New state           Signal
X; ~GQ         Waiting             cancel
X; ~LMB        WaitGfx
X; MOVE        SelGfx              move
X; TIMER       SelGfx              timer
X; RMB         SelGfx                        removed
X
X.SelGfx
X        cmp.b   #IECLASS_TIMER,ie_Class(a1)    ; Timer event?
X        bne     .sg_RAWKEY
X
X;Handle timer event
X        move.l  _timersignal,d0
X        bra     SignalTask
X
X.sg_RAWKEY
X        cmp.b   #IECLASS_RAWKEY,ie_Class(a1)   ; RAWKEY?
X        bne     .sg_LMB_UP
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  ie_Qualifier(a1),d0
X        and.w   _gfxqual,d0                    ; GQ?
X        bne     .EventHandled                  ; Still down, continue
X
X;Handle event ~GQ
X.sg_GQ
X        move.w  #cancel,_action                ; cancelling snap
X        move.w  #killbutt,_state               ; Kill obsolete button
X        move.l  _cancelsignal,d0               ; snap cancelled
X        bra     SignalTask
X
X.sg_LMB_UP
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        move.w  ie_Qualifier(a1),d0            ; Might be GQ going up.
X        and.w   _gfxqual,d0                    ; GQ?
X        beq     .sg_GQ
X
X        cmp.w   #LMB_UP,ie_Code(a1)            ; SELECTUP
X        bne     .sg_MOVE                       ; no, check for move
X
X; Handle event ~LMB
X        move.w  #waitgfx,_state                ; state waitext
X        bra     KillEvent
X
X.sg_MOVE
X        cmp.w   #NO_BUTT,ie_Code(a1)           ; MOVE
X        bne     .EventHandled                  ; No, check for RMB
X        move.w  ie_Qualifier(a1),d0
X        and.w   #IEQUALIFIER_RELATIVEMOUSE,d0  ; RELATIVEMOUSE
X        beq     .EventHandled
X
X; Handle event MOVE
X        move.l  _movesignal,d0
X        bra     SignalTask
X
X.sg_RMB
X        bra     KillEvent
X
X; ***************************************************
X; State WaitGfx     GQ
X; Actions     New state           Signal
X; ~GQ         Waiting             done
X; LMB         SelGfx              click
X; MOVE        WaitGfx                            No action needed.
X; TIMER       WaitGfx             timer
X; RMB         WaitGfx             --             remove event
X
X.WaitGfx
X        cmp.b   #IECLASS_TIMER,ie_Class(a1)    ; Timer event?
X        bne     .wg_RAWKEY
X
X;Handle timer event
X        move.l  _timersignal,d0
X        bra     SignalTask
X
X.wg_RAWKEY
X        cmp.b   #IECLASS_RAWKEY,ie_Class(a1)   ; RAWKEY?
X        bne     .wg_LMB
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  ie_Qualifier(a1),d0
X        and.w   _gfxqual,d0                    ; GQ?
X        bne     .EventHandled                  ; Still down, continue
X
X;Handle event ~GQ
X.wg_GQ
X        move.w  #noaction,_action              ; no action
X        move.w  #waiting,_state                ; state waiting
X        move.l  _donesignal,d0                 ; snap finished
X        bra     SignalTask
X
X.wg_LMB
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        move.w  ie_Qualifier(a1),d0            ; Might be GQ going up
X        and.w   _gfxqual,d0                    ; GQ?
X        beq     .wg_GQ
X
X        cmp.w   #LMB,ie_Code(a1)               ; SELECTDOWN
X        bne     .wg_RMB                          ; no -- finished
X
X; Handle event LMB
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        move.w  #selgfx,_state                 ; state waitext
X        move.l  _clicksignal,d0                ; Extend selection
X        bra     SignalTask
X
X.wg_RMB
X        cmp.w   #NO_BUTT,ie_Code(a1)           ; Any button?
X        beq     .EventHandled                  ; No, moves are ok
X
X        bra     KillEvent
X
X
X; ***************************************************
X; State CancelTxt
X; Make sure that the Button Up event that we've got
X; hanging around doesn't get through.
X
X.CancelTxt
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, just pass it on
X
X        cmp.w   #LMB_UP,ie_Code(a1)            ; The button?
X        bne     .EventHandled                  ; No
X
X        move.w  #noaction,_action
X        move.w  #waiting,_state
X        bra     KillEvent
X
X; ***************************************************
X; State Insert
X; Snap actions are removed, the rest are passed along.
X
X.Insert
X        cmp.b   #IECLASS_RAWMOUSE,ie_Class(a1) ; Is it RAWMOUSE?
X        bne     .EventHandled                  ; Nope, pass it on
X
X        cmp.w   #LMB,ie_Code(a1)               ; Left mouse button
X        bne     .insert_TQ                     ; No -- just kill
X
X        move.w  #noaction,_action              ; Cancel paste
X
X.insert_TQ
X        move.w  ie_Qualifier(a1),d0
X        and.w   _textqual,d0                   ; TQ?
X        beq     .EventHandled                  ; no
X
X        cmp.w   #NO_BUTT,ie_Code(a1)           ; Any button?
X        beq     .EventHandled                  ; No, moves are ok
X
XKillEvent:
X        move.b  #IECLASS_NULL,ie_Class(a1)     ; Kill event
X        bra     .EventHandled
X
XSignalTask:
X        bsr     Signal
X
X.EventHandled
X        move.l  ie_NextEvent(a1),a1             ; Get next event
X        bra     .nextevent
X
X.done
X        movem.l (sp)+,a4
X        move.l  a0,d0
X        rts
X
XSignal:
X        movem.l a0-a2/a6,-(sp)
X        move.l  _MyTask,a1
X        move.l  _SysBase,a6                     ;  Get ExecBase for Signal
X        jsr     _LVOSignal(a6)
X        movem.l (sp)+,a0-a2/a6
X        rts
X
X        end
END_OF_FILE
if test 17796 -ne `wc -c <'source/handler.s'`; then
    echo shar: \"'source/handler.s'\" unpacked with wrong size!
fi
# end of 'source/handler.s'
fi
if test -f 'source/minrexx.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'source/minrexx.c'\"
else
echo shar: Extracting \"'source/minrexx.c'\" \(15411 characters\)
sed "s/^X//" >'source/minrexx.c' <<'END_OF_FILE'
X#ifdef SNAPREXX
X/*
X *   This is an example of how REXX messages might be handled.  This is
X *   a `minimum' example that both accepts asynchronous REXX messages and
X *   can request REXX service.
X *
X *   Read this entire file!  It's short enough.
X *
X *   It is written in such a fashion that it can be attached to a program
X *   with a minimum of fuss.  The only external symbols it makes available
X *   are the seven functions and RexxSysBase.
X *
X *   This code is by Radical Eye Software, but it is put in the public
X *   domain.  I would appreciate it if the following string was left in
X *   both as a version check and as thanks from you for the use of this
X *   code.
X *
X *   If you modify this file for your own use, don't bump the version
X *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
X *   don't have fake `versions' floating around.
X */
Xstatic char *blurb = "Radical Eye MinRexx 0.4" ;
X/*
X *   We read in our own personal little include.
X */
X#include "minrexx.h"
X/*
X *   All of our local globals, hidden from sight.
X */
Xstatic struct MsgPort *rexxPort ;          /* this is *our* rexx port */
Xstatic int bringerdown ;                   /* are we trying to shut down? */
Xstatic struct rexxCommandList *globalrcl ; /* our command association list */
Xstatic long stillNeedReplies ;             /* how many replies are pending? */
Xstatic long rexxPortBit ;                  /* what bit to wait on for Rexx? */
Xstatic char *extension ;                   /* the extension for macros */
Xstatic int (*userdisp)() ;                 /* the user's dispatch function */
Xstatic struct RexxMsg *oRexxMsg ;          /* the outstanding Rexx message */
X/*
X *   Our library base.  Don't you dare close this!
X */
Xstruct RxsLib *RexxSysBase ;
X/*
X *   This is the main entry point into this code.
X */
Xlong upRexxPort(s, rcl, exten, uf)
X/*
X *   The first argument is the name of your port to be registered;
X *   this will be used, for instance, with the `address' command of ARexx.
X */
Xchar *s ;
X/*
X *   The second argument is an association list of command-name/user-data
X *   pairs.  It's an array of struct rexxCommandList, terminated by a
X *   structure with a NULL in the name field. The commands are case
X *   sensitive.  The user-data field can contain anything appropriate,
X *   perhaps a function to call or some other data.
X */
Xstruct rexxCommandList *rcl ;
X/*
X *   The third argument is the file extension for ARexx macros invoked
X *   by this program.  If you supply this argument, any `primitive' not
X *   in the association list rcl will be sent out to ARexx for
X *   interpretation, thus allowing macro programs to work just like
X *   primitives.  If you do not want this behavior, supply a `NULL'
X *   here, and those commands not understood will be replied with an
X *   error value of RXERRORNOCMD.
X */
Xchar *exten ;
X/*
X *   The fourth argument is the user dispatch function.  This function
X *   will *only* be called from rexxDisp(), either from the user calling
X *   this function directly, or from dnRexxPort().  Anytime a command
X *   match is found in the association list, this user-supplied function
X *   will be called with two arguments---the Rexx message that was
X *   received, and a pointer to the association pair.  This function
X *   should return a `1' if the message was replied to by the function
X *   and a `0' if the default success code of (0, 0) should be returned.
X *   Note that the user function should never ReplyMsg() the message;
X *   instead he should indicate the return values with replyRexxCmd();
X *   otherwise we lose track of the messages that still lack replies.
X */
Xint (*uf)() ;
X/*
X *   upRexxPort() returns the signal bit to wait on for Rexx messages.
X *   If something goes wrong, it simply returns a `0'.  Note that this
X *   function is safe to call multiple times because we check to make
X *   sure we haven't opened already.  It's also a quick way to change
X *   the association list or dispatch function.
X */
X{
X   struct MsgPort *FindPort() ;
X   struct MsgPort *CreatePort() ;
X
X/*
X *   Some basic error checking.
X */
X   if (rcl == NULL || uf == NULL)
X      return(0L) ;
X/*
X *   If we aren't open, we make sure no one else has opened a port with
X *   this name already.  If that works, and the createport succeeds, we
X *   fill rexxPortBit with the value to return.
X *
X *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
X *   for rexxPort == NULL also insures that our rexxPortBit is 0.
X */
X   if (rexxPort == NULL) {
X      Forbid() ;
X      if (FindPort(s)==NULL)
X         rexxPort = CreatePort(s, 0L) ;
X      Permit() ;
X      if (rexxPort != NULL)
X         rexxPortBit = 1L << rexxPort->mp_SigBit ;
X   }
X/*
X *   Squirrel away these values for our own internal access, and return
X *   the wait bit.
X */
X   globalrcl = rcl ;
X   extension = exten ;
X   userdisp = uf ;
X   return(rexxPortBit) ;
X}
X/*
X *   This function closes the rexx library, but only if it is open
X *   and we aren't expecting further replies from REXX.  It's
X *   *private*, but it doesn't have to be; it's pretty safe to
X *   call anytime.
X */
Xstatic void closeRexxLib() {
X   if (stillNeedReplies == 0 && RexxSysBase) {
X      CloseLibrary(RexxSysBase) ;
X      RexxSysBase = NULL ;
X   }
X}
X/*
X *   This function closes down the Rexx port.  It is always safe to
X *   call, and should *definitely* be made a part of your cleanup
X *   routine.  No arguments and no return.  It removes the Rexx port,
X *   replies to all of the messages and insures that we get replies
X *   to all the ones we sent out, closes the Rexx library, deletes the
X *   port, clears a few flags, and leaves.
X */
Xvoid dnRexxPort() {
X   if (rexxPort) {
X      RemPort(rexxPort) ;
X      bringerdown = 1 ;
X/*
X *   A message still hanging around?  We kill it off.
X */
X      if (oRexxMsg) {
X         oRexxMsg->rm_Result1 = RXERRORIMGONE ;
X         ReplyMsg(oRexxMsg) ;
X         oRexxMsg = NULL ;
X      }
X      while (stillNeedReplies) {
X         WaitPort(rexxPort) ;
X         dispRexxPort() ;
X      }
X      closeRexxLib() ;
X      DeletePort(rexxPort) ;
X      rexxPort = NULL ;
X   }
X   rexxPortBit = 0 ;
X}
X/*
X *   Here we dispatch any REXX messages that might be outstanding.
X *   This is the main routine for handling Rexx messages.
X *   This function is fast if no messages are outstanding, so it's
X *   pretty safe to call fairly often.
X *
X *   If we are bring the system down and flushing messages, we reply
X *   with a pretty serious return code RXERRORIMGONE.
X *
X *   No arguments, no returns.
X */
Xvoid dispRexxPort() {
X   register struct RexxMsg *RexxMsg ;
X   int cmdcmp() ;
X   register struct rexxCommandList *rcl ;
X   register char *p ;
X   register int dontreply ;
X
X/*
X *   If there's no rexx port, we're out of here.
X */
X   if (rexxPort == NULL)
X      return ;
X/*
X *   Otherwise we have our normal loop on messages.
X */
X   while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
X/*
X *   If we have a reply to a message we sent, we look at the second
X *   argument.  If it's set, it's a function we are supposed to call
X *   so we call it.  Then, we kill the argstring and the message
X *   itself, decrement the outstanding count, and attempt to close
X *   down the Rexx library.  Note that this call only succeeds if
X *   there are no outstanding messages.  Also, it's pretty quick, so
X *   don't talk to me about efficiency.
X */
X      if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
X         if (RexxMsg->rm_Args[1]) {
X            ((int (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ;
X         }
X         DeleteArgstring(RexxMsg->rm_Args[0]) ;
X         DeleteRexxMsg(RexxMsg) ;
X         stillNeedReplies-- ;
X         closeRexxLib() ;
X/*
X *   The default case is we got a message and we need to check it for
X *   primitives.  We skip past any initial tabs or spaces and initialize
X *   the return code fields.
X */
X      } else {
X         p = (char *)RexxMsg->rm_Args[0] ;
X         while (*p > 0 && *p <= ' ')
X            p++ ;
X         RexxMsg->rm_Result1 = 0 ;
X         RexxMsg->rm_Result2 = 0 ;
X/*
X *   If somehow the reply is already done or postponed, `dontreply' is
X *   set.
X */
X         dontreply = 0 ;
X/*
X *   If the sky is falling, we just blow up and replymsg.
X */
X         if (bringerdown) {
X            RexxMsg->rm_Result1 = RXERRORIMGONE ;
X/*
X *   Otherwise we cdr down our association list, comparing commands,
X *   until we get a match.  If we get a match, we call the dispatch
X *   function with the appropriate arguments, and break out.
X */
X         } else {
X            oRexxMsg = RexxMsg ;
X            for (rcl = globalrcl; rcl->name; rcl++) {
X               if (cmdcmp(rcl->name, p) == 0) {
X                  userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ;
X                  break ;
X               }
X            }
X/*
X *   If we broke out, rcl will point to the command we executed; if we
X *   are at the end of the list, we didn't understand the command.  In
X *   this case, if we were supplied an extension in upRexxPort, we know
X *   that we should send the command out, so we do so, synchronously.
X *   The synchronous send takes care of our reply.  If we were given a
X *   NULL extension, we bitch that the command didn't make sense to us.
X */
X            if (rcl->name == NULL) {
X               if (extension) {
X                  syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
X                  dontreply = 1 ;
X               } else {
X                  RexxMsg->rm_Result1 = RXERRORNOCMD ;
X               }
X            }
X         }
X/*
X *   Finally, reply if appropriate.
X */
X         oRexxMsg = NULL ;
X         if (! dontreply)
X            ReplyMsg(RexxMsg) ;
X      }
X   }
X}
X/*
X *   This is the function we use to see if the command matches
X *   the command string.  Not case sensitive, and the real command only
X *   need be a prefix of the command string.  Make sure all commands
X *   are given in lower case!
X */
Xstatic int cmdcmp(c, m)
Xregister char *c, *m ;
X{
X   while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
X      c++ ;
X      m++ ;
X   }
X   return(*c) ;
X}
X/*
X *   Opens the Rexx library if unopened.  Returns success (1) or
X *   failure (0).  This is another function that is *private* but
X *   that doesn't have to be.
X */
Xstatic int openRexxLib() {
X   if (RexxSysBase)
X      return(1) ;
X   return((RexxSysBase = (struct RxsLib *)OpenLibrary(RXSNAME, 0L)) != NULL) ;
X}
X/*
X *   This is the general ARexx command interface, but is not the one
X *   you will use most of the time; ones defined later are easier to
X *   understand and use.  But they all go through here.
X */
Xstruct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
Xchar *s ;
X/*
X *   The first parameter is the command to send to Rexx.
X */
Xint (*f)() ;
X/*
X *   The second parameter is either NULL, indicating that the command
X *   should execute asynchronously, or a function to be called when the
X *   message we build up and send out here finally returns.  Please note
X *   that the function supplied here could be called during cleanup after
X *   a fatal error, so make sure it is `safe'.  This function always is
X *   passed one argument, the RexxMsg that is being replied.
X */
XSTRPTR p1, p2, p3 ;
X/*
X *   These are up to three arguments to be stuffed into the RexxMsg we
X *   are building up, making the values available when the message is
X *   finally replied to.  The values are stuffed into Args[2]..Args[4].
X */
X{
X   struct RexxMsg *CreateRexxMsg() ;
X   STRPTR CreateArgstring() ;
X   register struct MsgPort *rexxport ;
X   register struct RexxMsg *RexxMsg ;
X
X/*
X *   If we have too many replies out there, we just return failure.
X *   Note that you should check the return code to make sure your
X *   message got out!  Then, we forbid, and make sure that:
X *      - we have a rexx port open
X *      - Rexx is out there
X *      - the library is open
X *      - we can create a message
X *      - we can create an argstring
X *
X *   If all of these succeed, we stuff a few values and send the
X *   message, permit, and return.
X */
X   if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
X      return(NULL) ;
X   RexxMsg = NULL ;
X   if (openRexxLib() && (RexxMsg =
X             CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
X             (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
X      RexxMsg->rm_Action = RXCOMM ;
X      RexxMsg->rm_Args[1] = (STRPTR)f ;
X      RexxMsg->rm_Args[2] = p1 ;
X      RexxMsg->rm_Args[3] = p2 ;
X      RexxMsg->rm_Args[4] = p3 ;
X      RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
X      Forbid() ;
X      if (rexxport = FindPort(RXSDIR))
X         PutMsg(rexxport, RexxMsg) ;
X      Permit() ;
X      if (rexxport) {
X         stillNeedReplies++ ;
X         return(RexxMsg) ;
X      } else
X         DeleteArgstring(RexxMsg->rm_Args[0]) ;
X   }
X   if (RexxMsg)
X      DeleteRexxMsg(RexxMsg) ;
X   closeRexxLib() ;
X   return(NULL) ;
X}
X/*
X *   This function is used to send out an ARexx message and return
X *   immediately.  Its single parameter is the command to send.
X */
Xstruct RexxMsg *asyncRexxCmd(s)
Xchar *s ;
X{
X   return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
X}
X/*
X *   This function sets things up to reply to the message that caused
X *   it when we get a reply to the message we are sending out here.
X *   But first the function we pass in, which actually handles the reply.
X *   Note how we get the message from the Args[2]; Args[0] is the command,
X *   Args[1] is this function, and Args[2]..Args[4] are any parameters
X *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
X *   along.
X */
Xstatic void replytoit(msg)
Xregister struct RexxMsg *msg ;
X{
X   register struct RexxMsg *omsg ;
X
X   omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
X   replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
X   ReplyMsg(omsg) ;
X}
X/*
X *   This function makes use of everything we've put together so far,
X *   and functions as a synchronous Rexx call; as soon as the macro
X *   invoked here returns, we reply to `msg', passing the return codes
X *   back.
X */
Xstruct RexxMsg *syncRexxCmd(s, msg)
Xchar *s ;
Xstruct RexxMsg *msg ;
X{
X   return(sendRexxCmd(s, (APTR)&replytoit, msg, NULL, NULL)) ;
X}
X/*
X *   There are times when you want to pass back return codes or a
X *   return string; call this function when you want to do that,
X *   and return `1' from the user dispatch function so the main
X *   event loop doesn't reply (because we reply here.)  This function
X *   always returns 1.
X */
Xvoid replyRexxCmd(msg, primary, secondary, string)
X/*
X *   The first parameter is the message we are replying to.
X */
Xregister struct RexxMsg *msg ;
X/*
X *   The next two parameters are the primary and secondary return
X *   codes.
X */
Xregister long primary, secondary ;
X/*
X *   The final parameter is a return string.  This string is only
X *   returned if the primary return code is 0, and a string was
X *   requested.
X *
X *   We also note that we have replied to the message that came in.
X */
Xregister char *string ;
X{
X   STRPTR CreateArgstring() ;
X
X/*
X *   Note how we make sure the Rexx Library is open before calling
X *   CreateArgstring . . . and we close it down at the end, if possible.
X */
X   if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
X      if (string && openRexxLib())
X         secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
X      else
X         secondary = 0L ;
X   }
X   msg->rm_Result1 = primary ;
X   msg->rm_Result2 = secondary ;
X   closeRexxLib() ;
X}
X#endif
END_OF_FILE
if test 15411 -ne `wc -c <'source/minrexx.c'`; then
    echo shar: \"'source/minrexx.c'\" unpacked with wrong size!
fi
# end of 'source/minrexx.c'
fi
if test -f 'source/snapchars.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'source/snapchars.c'\"
else
echo shar: Extracting \"'source/snapchars.c'\" \(21526 characters\)
sed "s/^X//" >'source/snapchars.c' <<'END_OF_FILE'
X/* Auto: make
X*/
X
XIMPORT struct SnapRsrc *SnapRsrc;
X
X#define COPY 0xC0L
X#define INVCOPY 0x30L
X#define CopyChar(_x, _y, _m)                                  \
X    BltBitMap(&MyBM, (LONG)_x, (LONG)_y,                      \
X      &TempBM, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight,     \
X      _m, -1L, NULL);                                         \
X    WaitBlit()
X
XWORD Unit;
XWORD Pattern[5] = {
X    0,
X    0x0c3f,     /* Frame ....oo....oooooo */
X    0x3333,     /* Char  ..oo..oo..oo..oo */
X    0x1f1f,     /* Word  ...ooooo...ooooo */
X    0xffff      /* Line  oooooooooooooooo */
X};
X
XIMPORT LONG xl; /* leftmost x position */
XIMPORT LONG xr; /* rightmost x position */
XIMPORT LONG yt; /* topmost y position */
XIMPORT LONG yb; /* bottommost y position */
XLONG minx; /* left limit */
XLONG maxx; /* right limit */
XLONG maxy; /* bottom limit */
XLONG tl, tr; /* used by findword - left and right edge of word */
XWORD fw, fh; /* Font width and height used when drawing the frame */
XWORD GZZ;
XWORD SBM;
X
XIMPORT LONG mx, my; /* Mouse position in character steps */
X#define closetop     0
X#define closebottom  1
XWORD closey;
X#define closeleft   0
X#define closeright  1
XWORD closex;
X
X
Xstruct Window *window;  /* The window we're snapping from */
X
X/* Data for font being snapped */
XUWORD FontHeight;
XUWORD FontWidth;
XUWORD Underscore;
XUBYTE FontType;
XUBYTE LoChar;
XUBYTE HiChar;
XUWORD Modulo;
XUWORD *CharLoc;
XUWORD NoOfChars;
XUBYTE *SrcData;
XIMPORT UBYTE *CharData;
XUBYTE IFlags;
X
XIMPORT struct RastPort TempRp, MyRP;
XIMPORT struct BitMap TempBM, MyBM;
X
XIMPORT UWORD *TempRaster;   /* Used for character recognition */
X
XIMPORT struct Screen *theScreen;
XIMPORT struct RastPort rp;
Xstruct Layer *LockedLayer;
X
XIMPORT LONGBITS cancelsignal, donesignal, movesignal, clicksignal, timersignal;
XIMPORT WORD action;
X
XWORD starting;
X
X/* Init vars with font data.
X*/
X
XVOID SetSnapFont(font)
Xstruct TextFont *font;
X{
X    if (!font) {
X        FontWidth = -1;
X        return;
X    }
X    FontHeight = font->tf_YSize;
X    Underscore = font->tf_Baseline + 1;
X    FontType = font->tf_Flags;
X    FontWidth = (FontType & FPF_PROPORTIONAL ? -1 : font->tf_XSize);
X    if (FontWidth == -1) {
X        return;
X    }
X    LoChar = font->tf_LoChar;
X    HiChar = font->tf_HiChar;
X    Modulo = font->tf_Modulo;
X    CharLoc = (UWORD *)font->tf_CharLoc;
X    NoOfChars = HiChar - LoChar + 1;
X    Modulo = font->tf_Modulo;
X    SrcData = (UBYTE *)font->tf_CharData;
X    BltClear(CharData, 256L * 32, 0L);
X    WaitBlit();
X    CopyFont();
X}
X
X/* Check if the character at x, y is a space
X*/
X
XWORD IsSpace(x, y)
XLONG x, y;
X{
X    REGISTER WORD i = FontHeight - 1;
X    REGISTER UWORD *data = &TempRaster[i];
X
X      /* Copy character at x, y */
X    BltClear((char *)TempRaster, 32L, 0L);
X    ClipBlit(&rp, x, y,
X      &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY);
X    WaitBlit();
X
X    if (*data) {         /* Try inverted copy */
X        ClipBlit(&rp, x, y,
X          &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, INVCOPY);
X        WaitBlit();
X    }
X    while (i--) {
X        if (*data--) {
X            return 0;
X        }
X    }
X    return 1;
X}
X
X#define ShortFrame 4L      /* Square frame - columnar select */
X#define LongFrame  8L      /* Strange frame - char or word select */
XIMPORT LONG OFType;        /* Old frame type: ShortFrame/LongFrame */
XIMPORT UWORD Ptrn;
XIMPORT Point OldFrame[];
XIMPORT Point NewFrame[];
X
X
X/* update_frame calculates the new frame,
X** erases the old frame and draws the new one.
X** It's all pretty obvious if you take it slowly.
X*/
XVOID update_frame()
X{
X    LONG ft;
X    switch (Unit) {
X        case UNIT_FRAME: {
X              /*********\
X              *         *
X              *         *
X              \*********/
X            NewFrame[0].x = xl - 1;  NewFrame[0].y = yt - 1;
X            NewFrame[1].x = xr + fw; NewFrame[1].y = yt - 1;
X            NewFrame[2].x = xr + fw; NewFrame[2].y = yb + fh;
X            NewFrame[3].x = xl - 1;  NewFrame[3].y = yb + fh;
X            NewFrame[4].x = xl - 1;  NewFrame[4].y = yt - 1;
X            ft = ShortFrame;
X            break;
X        }
X        case UNIT_CHAR:
X        case UNIT_WORD: {
X            if (yt == yb) {   /* On the same line - same as UNIT_FRAME */
X                NewFrame[0].x = xl - 1;  NewFrame[0].y = yt - 1;
X                NewFrame[1].x = xr + fw; NewFrame[1].y = yt - 1;
X                NewFrame[2].x = xr + fw; NewFrame[2].y = yb + fh;
X                NewFrame[3].x = xl - 1;  NewFrame[3].y = yb + fh;
X                NewFrame[4].x = xl - 1;  NewFrame[4].y = yt - 1;
X                ft = ShortFrame;
X            } else {
X                      /*****\
X                 ******     *
X                 *          *
X                 *      *****
X                 *******/
X                NewFrame[0].x = xl - 1;    NewFrame[0].y = yt - 1;
X                NewFrame[1].x = maxx + fw; NewFrame[1].y = yt - 1;
X                NewFrame[2].x = maxx + fw; NewFrame[2].y = yb;
X                NewFrame[3].x = xr + fw;   NewFrame[3].y = yb;
X                NewFrame[4].x = xr + fw;   NewFrame[4].y = yb + fh;
X                NewFrame[5].x = minx - 1;  NewFrame[5].y = yb + fh;
X                NewFrame[6].x = minx - 1;  NewFrame[6].y = yt + fh;
X                NewFrame[7].x = xl - 1;    NewFrame[7].y = yt + fh;
X                NewFrame[8].x = xl - 1;    NewFrame[8].y = yt - 1;
X                ft = LongFrame;
X            }
X            break;
X        }
X        case UNIT_LINE: {
X            NewFrame[0].x = minx - 1;  NewFrame[0].y = yt - 1;
X            NewFrame[1].x = maxx + fw; NewFrame[1].y = yt - 1;
X            NewFrame[2].x = maxx + fw; NewFrame[2].y = yb + fh;
X            NewFrame[3].x = minx - 1;  NewFrame[3].y = yb + fh;
X            NewFrame[4].x = minx - 1;  NewFrame[4].y = yt - 1;
X            ft = ShortFrame;
X            break;
X        }
X        default: {
X            break;
X        }
X    }
X    draw_frame(ft);
X}
X
XVOID FindWord()
X{
X      /* Must remove frame to be able to search for spaces */
X    WaitTOF();
X    erase_frame();
X    tl = mx;
X      /* Find a space to the left... */
X    while (!IsSpace(tl, my)) {
X        tl -= fw;
X        if (tl < minx) {
X            break;
X        }
X    }
X    tl += fw;
X    tr = mx;
X      /* ...and to the right */
X    while (!IsSpace(tr, my)) {
X        tr += fw;
X        if (tr + fw > maxx) {
X            break;
X        }
X    }
X    tr -= fw;
X    if (tr < tl) {
X        tl = xl;
X        tr = xr;
X    }
X}
X
X/* ChangeUnit cycles the unit of selection. The differents units
X   are: character, word and line.
X*/
X
XVOID ChangeUnit()
X{
X
X    switch (Unit) {
X        case UNIT_FRAME: {
X            Unit = UNIT_CHAR;
X            break;
X        }
X        case UNIT_CHAR: {
X            Unit = UNIT_WORD;
X            FindWord();
X            xl = tl;
X            xr = tr;
X            break;
X        }
X        case UNIT_WORD: {
X            Unit = UNIT_LINE;
X            xl = minx;
X            xr = maxx;
X            break;
X        }
X        case UNIT_LINE: {
X            Unit = UNIT_FRAME;
X            xl = xr = mx;
X            break;
X        }
X    }
X    if (SnapRsrc->CrawlPtrn == 0) {
X        Ptrn = Pattern[Unit];
X    }
X}
X
X/* ExtendSelection extends the current selection according to
X   the mouse position and the selected unit.
X   Note that ExtendSelection doesn't optimize moves that don't
X   make any difference. FIXME
X*/
X
XVOID ExtendSelection()
X{
X    /* Fix which row we're talking about */
X    if (closey == closetop) {       /* Find closest row */
X        yt = my;               /* change top row */
X    } else {
X        yb = my;               /* change bottom row */
X    }
X
X    /* Take care of left and right character pos */
X    switch (Unit) {
X        case UNIT_FRAME: {
X            if (closex == closeleft) {
X                xl = mx;
X            } else {
X                xr = mx;
X            }
X            break;
X        }
X        case UNIT_CHAR: {
X            if (yt == yb) {            /* One line */
X                if (closex == closeleft) {
X                    xl = mx;
X                } else {
X                    xr = mx;
X                }
X            } else {                    /* Multiple lines */
X                if (yt == my) {
X                    xl = mx;            /* At top - set left */
X                } else {
X                    xr = mx;            /* At bottom - set right */
X                }
X            }
X            break;
X        }
X        case UNIT_WORD: {
X            FindWord();                 /* Find the word */
X            if (yt == yb) {             /* One line */
X                if (closex == closeleft) {   /* Find closest char pos */
X                    xl = tl;
X                } else {
X                    xr = tr;
X                }
X            } else {                   /* Multiple lines */
X                if (yt == my) {        /* Where am I */
X                    xl = tl;           /* At top - set left */
X                } else {
X                    xr = tr;           /* At bottom - set right */
X                }
X            }
X            break;
X        }
X        case UNIT_LINE: {              /* Always full width */
X            break;
X        }
X    }
X    if (yt - fh == yb) {
X        yb += fh;
X    }
X    if (yt == yb && xl - fw == xr) {
X        xr += fw;
X    }
X    if (xr > maxx) {         /* Check for window bounds */
X        xr = maxx;
X    }
X    if (xl < minx) {         /* Check for window bounds */
X        xl = minx;
X    }
X    if (yb > maxy) {         /* Check for window bounds */
X        yb = maxy;
X    }
X}
X
X/* The actual character snapper. It actually works. :-) */
X
XWORD SnapChars()
X{
X    LONG width;
X    LONG height;
X    UBYTE *SnapSpace;
X    ULONG SnapSize;
X    ULONG counter;
X    REGISTER LONG x, y;
X
X      /* Check coordinates */
X    if (yt - fh == yb) {        /* No rows, shouldn't happen */
X        return 0;
X    }
X    if (yt == yb && xl - fw == xr) {     /* Nothing at all */
X        return 0;
X    }
X
X      /* Calculate stuff */
X    width = maxx - (minx + 1) + fw + fw;  /* Add one for a LF */
X    height = yb - yt + fh;
X    SnapSize = ((width / fw) + 1) * (height / fh);
X    counter = 0;
X
X      /* Initialize things */
X    InitRastPort(&MyRP);
X    InitBitMap(&MyBM, 1L, width, height);
X    MyRP.BitMap = &MyBM;
X    SnapSpace = AllocMem(SnapSize, MEMF_PUBLIC|MEMF_CLEAR);
X      /* Please insert more memory */
X    if (!SnapSpace) {
X        return 0;
X    }
X    MyBM.Planes[0] = AllocRaster(width, height);
X    if (!MyBM.Planes[0]) {
X        FreeMem(SnapSpace, SnapSize);
X        return 0;
X    }
X    IFlags = 0;
X      /* Make a local copy of the snapped chars */
X    ClipBlit(&rp, minx, yt, &MyRP, 0L, 0L, width, height, COPY);
X
X      /* Ok, now we've got a copy of the character data */
X      /* Now it's ok to mess with the layers again */
X    UnlockLayer(LockedLayer);
X
X      /* Clear our work area */
X    BltClear((char *)TempRaster, 32L, 0L);
X
X      /* Calculate bounds */
X    xl -= minx;
X    xr -= minx;
X    maxx -= minx;
X    minx = 0;
X    yb -= yt;
X    yt = 0;
X
X      /* Single line - needs to be handled separately */
X    if (yt == yb) { /* Ok, we've got one */
X
X          /* Read from left to right */
X        for (x = xl; x <= xr; x += fw, counter++) {
X            CopyChar(x, yt, COPY);
X            if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
X                SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
X            }
X        }
X        if (Unit == UNIT_LINE) {
X            while (counter && SnapSpace[counter-1] == ' ') {
X                counter--;
X            }
X        }
X    } else { /* Multiple lines */
X
X        if (Unit == UNIT_FRAME) {
X            minx = xl;
X            maxx = xr;
X        }
X
X          /* Read first line */
X        for (x = xl; x <= maxx; x += fw, counter++) {
X            CopyChar(x, yt, COPY);
X            if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
X                SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
X            }
X        }
X        if (Unit == UNIT_FRAME) {
X            SnapSpace[counter++] = 10;
X        } else {
X            SHORT endspace = (SnapSpace[counter-1] == ' ');
X              /* Remove trailing blanks */
X            while (counter && SnapSpace[counter-1] == ' ') {
X                counter--;
X            }
X            if (endspace || !(SnapRsrc->flags & JOINLONG)) {
X                SnapSpace[counter++] = 10;
X            }
X        }
X
X          /* If more than two rows - read full middle rows */
X        if (yt + fh != yb) {
X            for (y = yt + fh; y < yb; y += fh) {
X                for (x = minx; x <= maxx; x += fw, counter++) {
X                    CopyChar(x, y, COPY);
X                    if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
X                        SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
X                    }
X                }
X                if (Unit == UNIT_FRAME) {
X                    SnapSpace[counter++] = 10;
X                } else {
X                    SHORT endspace = (SnapSpace[counter-1] == ' ');
X                      /* Remove trailing blanks */
X                    while (counter && SnapSpace[counter-1] == ' ') {
X                        counter--;
X                    }
X                    if (endspace || !(SnapRsrc->flags & JOINLONG)) {
X                        SnapSpace[counter++] = 10;
X                    }
X                }
X            }
X        }
X
X          /* Read last line */
X        for (x = minx; x <= xr; x += fw, counter++) {
X            CopyChar(x, yb, COPY);
X            if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
X                SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
X            }
X        }
X        /* Remove trailing blanks */
X        while (counter && SnapSpace[counter-1] == ' ') {
X            counter--;
X        }
X    }
X    FreeRaster(MyBM.Planes[0], width, height);
X    SaveClip(SnapSpace, counter);
X    FreeMem(SnapSpace, SnapSize);
X    return 1;
X}
X
X
X/* HandleChars is the part of the Snap state machine that handles
X   snapping of characters. The selection is done in different
X   units: char, word, line.
X*/
X
XWORD HandleChars()
X{
X    LONG xoff, yoff;
X    LONG ox, oy;
X
X      /* Find out which screen we're working on */
X    theScreen = WhichScreen();
X
X      /* Oops, no screen? */
X    if (!theScreen) {
X        action = noaction;
X        return 0;
X    }
X
X      /* Ok, what window? */
X    window = WhichWindow(theScreen);
X
X      /* Oh dear, no window. */
X    if (!window) {
X        action = noaction;
X        return 0;
X    }
X
X      /* No messing with the layers while I think */
X    LockedLayer = window->WLayer;
X    LockLayer(0L, LockedLayer);
X
X      /* Don't want to wreck somebody's rastport */
X    CopyMem((char *)window->RPort, (char *)&rp, (LONG)sizeof(struct RastPort));
X
X      /* Or his picture */
X    SetDrMd(&rp, COMPLEMENT);
X    rp.Mask = SnapRsrc->FrameMask;
X
X      /* Find out what we're trying to read */
X    SetSnapFont(rp.Font);
X    if (FontWidth == -1) {
X        UnlockLayer(LockedLayer);
X        action = noaction;
X        return 0;
X    }
X
X    if (window->Flags & GIMMEZEROZERO) {
X        GZZ = 1;
X    } else {
X        GZZ = 0;
X    }
X    if (window->Flags & SUPER_BITMAP) {
X        SBM = 1;
X    } else {
X        SBM = 0;
X    }
X
X      /* Find a position */
X    xl = (GZZ ? window->GZZMouseX : window->MouseX)
X      + window->RPort->Layer->Scroll_X;
X    yt = (GZZ ? window->GZZMouseY : window->MouseY)
X      + window->RPort->Layer->Scroll_Y;
X
X    if (xl < 0) {
X        xl = 0;
X    }
X    if (yt < 0) {
X        yt = 0;
X    }
X
X      /* Check your position */
X    if (xl > (GZZ ? window->GZZWidth : window->Width) ||
X      yt > (GZZ ? window->GZZHeight : window->Height)) {
X        UnlockLayer(LockedLayer);
X        action = noaction;
X        return 0;
X    }
X    IFlags = 0;
X
X      /* Find out the offset for the clicked character, if any.
X      ** This is the part that makes it special. Simple, isn't it. Hah!
X      */
X    {
X        REGISTER struct CacheWindow *cw = GetCachedWindow(theScreen, window);
X
X        if (cw) {
X            xoff = - ((xl - cw->xoff) % cw->fw);
X            yoff = - ((yt - cw->yoff) % cw->fh);
X            BltClear((char *)TempRaster, 32L, 0L);
X            ClipBlit(&rp, xl + xoff, yt + yoff,
X              &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY);
X            WaitBlit();
X            if (interpret(TempRaster) != 255) {
X                goto found;
X            }
X        }
X          /* No cache or cache didn't match */
X        xl -= 7;
X        yt -= 7;
X        BltClear((char *)TempRaster, 32L, 0L);
X        xoff = 0;
X        while (xoff < (16 - FontWidth)) {
X            ClipBlit(&rp, xl + xoff, yt,
X              &TempRp, 0L, 0L, (LONG)FontWidth, 16L, COPY);
X            WaitBlit();
X            yoff = 0;
X            while (yoff < (16 - FontHeight)) {
X                if (interpret(&TempRaster[yoff]) != 255) {
X                    goto found;
X                }
X                ++yoff;
X            }
X            ++xoff;
X        }
X
X          /* No character found. Back off */
X        UnlockLayer(LockedLayer);
X        action = noaction;
X        return 0;
X
Xfound:
X          /* Ok, now we know where to look for chars.
X          ** xoff and yoff is character position within our 16x16 bitmap.
X          */
X        xl = xl + xoff;         /* Adjust x */
X        yt = yt + yoff;         /* Adjust y */
X
X        fw = FontWidth;
X        fh = FontHeight;
X
X        {
X            SHORT temp = fh;
X            while (temp <= fh + 1) {  /* Check for extra pixel row */
X                BltClear((char *)TempRaster, 32L, 0L);
X                ClipBlit(&rp, xl, yt + temp,
X                  &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY);
X                WaitBlit();
X                if (interpret(TempRaster) != 255) {
X                    fh = temp;
X                    break;
X                }
X                ++temp;
X            }
X        }
X
X          /* Find out offsets within the window */
X        xoff = xl % fw;
X        yoff = yt % fh;
X
X        if (cw) {
X            cw->xoff = xoff;
X            cw->yoff = yoff;
X            cw->fw = fw;
X            cw->fh = fh;
X        } else {
X            CacheWindow(window, xoff, yoff, fw, fh);
X        }
X    }
X
X      /* Set bounds */
X    minx = xoff;
X    maxx = minx +
X      (((GZZ ?
X        window->GZZWidth :
X        window->Width - window->BorderRight
X          /* Hack for borderless conman windows */
X        + (window->Flags & BORDERLESS && window->Flags & WINDOWSIZING ? 14 : 0))
X      - minx - fw) / fw) * fw;
X    maxy = ((GZZ ? window->GZZHeight : window->Height) / fh) * fh;
X
X      /* Check bounds */
X    if (xl > maxx) {
X        UnlockLayer(LockedLayer);
X        action = noaction;
X        return 0;
X    }
X      /* Set box dimensions */
X    xr = xl;
X    yb = yt;
X    ox = xr;
X    oy = yt;
X
X      /* Select unit while starting */
X    starting = 1;
X
X      /* Starting unit is character or frame */
X    Unit = SnapRsrc->StartUnit;
X    Ptrn = (SnapRsrc->CrawlPtrn ? SnapRsrc->CrawlPtrn : Pattern[Unit]);
X    OFType = 0L;
X    update_frame();
X
X      /* Get the state machine running */
X    FOREVER {
X          /* Wait for something to happen */
X        REGISTER LONGBITS sig =
X          Wait(movesignal|cancelsignal|donesignal|clicksignal|timersignal);
X
X        if ((sig & timersignal) && (SnapRsrc->CrawlPtrn != 0xffff)) {
X            crawl_frame(0L);
X        }
X
X        mx = (LONG)(GZZ ? window->GZZMouseX : window->MouseX)
X          + window->RPort->Layer->Scroll_X;
X        if (mx < 0) {
X            mx = 0;
X        }
X          /* Calculate which edge is closest */
X        if ((mx - xl) < (xr - mx)) {
X            closex = closeleft;
X        } else {
X            closex = closeright;
X        }
X          /* Only interested in real char pos */
X        mx = mx - ((mx - xoff) % fw);
X
X        my = (LONG)(GZZ ? window->GZZMouseY : window->MouseY)
X          + window->RPort->Layer->Scroll_Y;
X        if (my < 0) {
X            my = 0;
X        }
X          /* Calculate which row is closest */
X        if ((my - yt) < (yb - my)) {
X            closey = closetop;
X        } else {
X            closey = closebottom;
X        }
X        my = my - ((my - yoff) % fh);
X
X          /* Hey, it moves! It's alive!! */
X        if ((sig & movesignal) && (action == snaptext)) {
X            if (mx != ox || my != oy) {  /* Something's happened */
X                ExtendSelection();
X                update_frame();
X                starting = 0;
X                ox = mx;
X                oy = my;
X                sig &= ~clicksignal;
X            }
X        }
X
X          /* Ok, forget it... */
X        if (sig & cancelsignal) {
X            erase_frame();
X            UnlockLayer(LockedLayer);
X            return 0;
X        }
X
X          /* Click */
X        if ((sig & clicksignal) && (action == snaptext)) {
X              /* Selecting unit */
X            if (starting) {
X                if (mx == ox && my == oy) {
X                    ChangeUnit();
X                    if (Unit == UNIT_CHAR) {
X                        ChangeUnit();
X                    }
X                    update_frame();
X                } else if (Unit == UNIT_FRAME) {
X                    ChangeUnit();
X                    update_frame();
X                }
X            }
X            if (mx != ox || my != oy) { /* Click in a new place */
X                ExtendSelection();
X                update_frame();
X                starting = 0;
X                ox = mx;
X                oy = my;
X            }
X        }
X
X          /* Finished */
X        if (sig & donesignal) {
X            erase_frame();
X            return SnapChars();
X        }
X    }
X}
X
END_OF_FILE
if test 21526 -ne `wc -c <'source/snapchars.c'`; then
    echo shar: \"'source/snapchars.c'\" unpacked with wrong size!
fi
# end of 'source/snapchars.c'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general dicussion to comp.sys.amiga.