(*---------------------------------------------------------------------------
  :Program.    Hook
  :Author.     Thomas Igracki
  :Address.    Obstallee 45, 1000 Berlin 20, W-Germany
  :E-Mail.     T.IGRACKI@BAMP.ZER
  :Version.    1.11
  :Date.       08-Jun-1992
  :Copyright.  Me
  :Language.   Oberon
  :Translator. Amiga Oberon 2.14d
  :Contents.   Diese Programm ändert den globalen EditHook für StringGadgets.
  :Usage.      Run Hook
  :History.    V1.1 : Hook unterstützt nun Copy&Paste (wie auch in Shells)!
  :History.    V1.11: Alt-g macht aus einem kleinen einen großen Buchstaben & vv
  :History.    V1.11: und geht zum nächsten Buchtaben
  :History.    V1.12: Falls Hook ein 2. Mal gestartet wird, wird er entfernt!
  :History.    V1.12: Bug in Alt-g behoben (öäü wurden nicht unterstützt)
  :History.    V1.13: Copy&Paste funktionierte nicht mehr
  :Remark.     OS2.0 Only!
  :Remark.     Ohne -d compilieren/linken! Es kann aber mit -s gelinkt werden!
---------------------------------------------------------------------------*)
MODULE Hook; (* $ClearVars- *)
IMPORT
   I: Intuition, d: Dos, s: SYSTEM, u: Utility, e: Exec,
   ie: InputEvent, st: Strings, con: Console, ConUnit;
TYPE
   LONGPTR = POINTER TO LONGINT;
CONST
   Ver = '$VER:\[33;1mHook V1.13\[m (05 Aug 1992) by Thomas Igracki';
VAR
   ConIO  : e.IOStdReq;
   oldHook: u.HookPtr;
   myHook : STRUCT (minNode* : e.MinNode)
              entry *: PROCEDURE (hook{8}: u.HookPtr;
                                  object{10}: e.APTR;
                                  message{9}: e.APTR): LONGINT;
              subEntry*: e.TaskPtr; (* it's bad, but usefull! *)
              data    *: e.ADDRESS;
            END;
   strPtr : e.STRPTR;

(* wenig verändertes Insert() aus Strings.mod *)
PROCEDURE Insert (VAR s : ARRAY OF CHAR;
               at,maxLen: INTEGER; str: ARRAY OF CHAR); (* $CopyArrays- *)
(* fügt 'str' in 's' an der Stelle 'at' eine. Beispiel:
   s = "Hallo"; Insert(s,3,20,"liHal");  dies ergibt "HalliHallo".
   maxLen: Länge von s *)

VAR l1,l2,i: INTEGER;
BEGIN
  l1 := st.Length(s); l2 := st.Length(str);
  IF l2 > maxLen-l1 THEN l2 := maxLen-l1 END;
  i := l1; WHILE (i>=at) DO         s[i+l2] := s  [i]; DEC(i) END;
  i := l2; WHILE (i>  0) DO DEC(i); s[at+i] := str[i]         END;
END Insert;

(* $SaveRegs+ $StackChk- $ClearVars- *)
PROCEDURE * HookFunc (hook{8}: u.HookPtr;
                      object{10}: e.APTR; message{9}: e.APTR): LONGINT;
VAR sg: I.SGWorkPtr; com: LONGPTR; work,undo: e.STRPTR; p,i: INTEGER; c: CHAR;
    Changed, back: BOOLEAN;
BEGIN
  com := message; sg := object; Changed := FALSE;
  IF (com^ = I.sghKey) & (sg.iEvent.class = ie.rawkey) THEN
     IF sg.iEvent.qualifier * {ie.lAlt,ie.rAlt} # {} THEN
        Changed := TRUE;
        CASE sg.iEvent.code OF
  (* t *)    014H : (* --- Swap the Chars under the cursor --- *)
                    work := sg.workBuffer; p := sg.bufferPos;
                    IF (p > 0) & (work^[p] # 0X) THEN
                       c := work^[p]; work^[p] := work^[p-1]; work^[p-1] := c
                    END;
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
  (* g *)    024H : (* --- Change case of the letter under the cursor --- *)
                    work := sg.workBuffer; p := sg.bufferPos;
                    IF ((work[p] >= 'A') & (work[p] <= 'Z')) OR 
                       (work[p] = 'Ä') OR (work[p] = 'Ö') OR (work[p] = 'Ü') THEN
                        work[p] := CHR(ORD(work[p])+32)
                    ELSIF ((work[p] >= 'a') & (work[p] <= 'z')) OR
                          (work[p] = 'ä') OR (work[p] = 'ö') OR (work[p] = 'ü') THEN
                        work[p] := CHR(ORD(work[p])-32)
                    END;
                    INC(sg.bufferPos);
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
  (* right*) 04EH : (* --- Go to the beginning of the next word  --- *)
                    work := sg.workBuffer; p := sg.bufferPos;
                    WHILE (work^[p] # ' ') & (work^[p] # 0X) DO INC(p) END;
                    WHILE work^[p] = ' ' DO INC(p) END;
                    sg.bufferPos := p;
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
  (* left *) 04FH : (* --- Go to the beginning of the prev word  --- *)
                    work := sg.workBuffer; p := sg.bufferPos;
                    IF p > 0 THEN DEC(p);
                       WHILE (work^[p] = ' ') & (p > 0) DO DEC(p) END;
                       WHILE (work^[p] # ' ') & (p > 0) DO DEC(p) END;
                       IF (work^[p] = ' ') & (work^[p+1] # ' ') & 
                          (sg.bufferPos # 1) THEN INC(p)
                       END;
                       sg.bufferPos := p;
                    END;
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
  (* BSpace*)041H : (* --- Delete the prev word --- *)
                    work := sg.workBuffer; p := sg.bufferPos; i := 0;
                    IF p > 0 THEN
                       INC(i); DEC(p);
                       IF (work^[p] = ' ') THEN
                          WHILE (work^[p] = ' ') & (p > 0) DO INC(i); DEC(p) END;
                          back := (work^[p] # ' ') & (p = 0);
                       ELSE
                          WHILE (work^[p] # ' ') & (p > 0) DO INC(i); DEC(p) END;
                          back := (work^[p] = ' ') & (p = 0);
                       END;
                       IF (p > 0) OR back THEN DEC(i); INC(p) END;
                       st.Delete (work^,p,i); sg.bufferPos := p;
                    END;
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
  (* DEL *)  046H : (* --- Delete the next word --- *)
                    work := sg.workBuffer; p := sg.bufferPos; i := 0;
                    IF work^[p] = ' ' THEN WHILE work^[p]=' ' DO INC(i);INC(p) END;
                    ELSE
                       WHILE (work^[p] # ' ') & (work^[p] # 0X) DO INC(i); INC(p) END;
                    END;
                    st.Delete (work^,sg.bufferPos,i);
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
  (* up *)   04CH : (* --- Activate prev String Gadget --- *)
                    sg.actions := LONGSET{I.sgaEnd,I.sgaPrevActive}|
  (* down *) 04DH : (* --- Activate next String Gadget --- *)
                    sg.actions := LONGSET{I.sgaEnd,I.sgaNextActive}|
        ELSE
           Changed := FALSE
        END; (* CASE *)
     ELSIF sg.iEvent.qualifier * {ie.rCommand,ie.lCommand} # {} THEN
        Changed := TRUE;
        CASE sg.iEvent.code OF
    (* c *)  033H : (* Copy *)
                    con.SetConSnip (sg.workBuffer);
                    sg.actions := LONGSET{I.sgaRedisplay}|
    (* v *)  034H : (* Paste *)
                    undo := s.VAL(e.APTR,con.GetConSnip()); work := sg.workBuffer;
                    Insert (work^,sg.bufferPos,sg.stringInfo.maxChars-1,undo^);
                    sg.actions := LONGSET{I.sgaUse,I.sgaRedisplay}|
        ELSE
           Changed := FALSE
        END; (* CASE *)
(*     ELSIF sg.iEvent.code = 044H THEN (* RETURN *)
        IF sg.iEvent.qualifier * {ie.rShift} # {} THEN
           sg.actions := LONGSET{I.sgaNextActive}
        ELSIF sg.iEvent.qualifier * {ie.lShift} # {} THEN
           sg.actions := LONGSET{I.sgaPrevActive}
        ELSE Changed := FALSE
        END*)
     END; (* IF *)
     IF NOT Changed THEN
        s.SETREG(0,u.CallHookPkt (oldHook,object,message));
     END;
     (* FALSE (0), wenn Kommando unbekannt *)
     RETURN s.VAL(SHORTINT,Changed)
  END;
END HookFunc; (* $StackChk= $ClearVars= *)

BEGIN
    (* $IF SmallData *) Bitte ohne SmallData (-d) compilieren (* $END *)

    myHook.data := s.ADR(Ver);
    (* Task merken, damit der alte Hook entfernt werden kann! *)
    myHook.subEntry := e.exec.thisTask; (* hihi *)
    myHook.entry := HookFunc; oldHook := I.SetEditHook (s.ADR(myHook));
    strPtr := oldHook.data;

    (* Pointer zum console.devices holen *)
    IF e.OpenDevice ('console.device', ConUnit.library, s.ADR(ConIO), LONGSET{}) # 0 THEN HALT(20) END;
    con.base := ConIO.device; e.CloseDevice(s.ADR(ConIO));
     
    IF strPtr^ = Ver THEN (* alte Version benachrichtigen *)
       (* d.PrintF('\x07%s already started, so, I must go!\n',s.VAL(LONGINT,strPtr)+5); *)
       e.Signal(s.VAL(e.APTR,oldHook.subEntry),LONGSET{d.ctrlC});
       HALT(5) 
    END;

    d.PrintF ('%s!\n',s.VAL(LONGINT,s.ADR(Ver))+5);
    s.SETREG(0, e.Wait(LONGSET{d.ctrlC})); (* Auf Ctrl-C warten! *)
    d.PrintF ('\[33;1mHook\[m removed!\n');

CLOSE
    s.SETREG(0,I.SetEditHook (oldHook))
END Hook.
