DIRECTORY MessageWindow, Rope, TEditDocument, TEditInputOps, TEditSelection, TextNode, TiogaOps, TIPUser, UserProfile, ViewerClasses; KeyboardTioga: CEDAR PROGRAM IMPORTS MessageWindow, Rope, TEditInputOps, TEditSelection, TiogaOps, TIPUser, UserProfile ={ ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; Location: TYPE = TiogaOps.Location; Where: TYPE = {begin, end, all}; Border: TYPE = Where[begin .. end]; Who: TYPE = {branch, node, line, compound, word, subword, char, cursel}; Level: TYPE = Who[branch .. char]; Op: TYPE = {toPoint, selectVanilla, selectPendingDelete, delete}; SelectOp: TYPE = Op[selectVanilla .. delete]; emptyNext: BOOL _ TRUE; curWhere: Where; curWho: Who; nullKbd: BOOL _ FALSE; NullKbd: PROC RETURNS [BOOL] --TIPUser.TIPPredicate-- ~ { RETURN [nullKbd]}; EmptyKbdNext: PROC RETURNS [BOOL] --TIPUser.TIPPredicate-- ~ { RETURN [emptyNext]}; KbdIgnore: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {NULL}; KbdNull: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {nullKbd _ TRUE}; KbdNotNull: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {nullKbd _ FALSE}; KbdBegin: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWhere _ begin; quit _ TRUE}; KbdEnd: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWhere _ end; quit _ TRUE}; KbdAll: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWhere _ all; quit _ TRUE}; KbdBranch: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ branch; quit _ TRUE}; KbdNode: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ node; quit _ TRUE}; KbdLine: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ line; quit _ TRUE}; KbdCompound: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ compound; quit _ TRUE}; KbdWord: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ word; quit _ TRUE}; KbdSubWord: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ subword; quit _ TRUE}; KbdChar: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ char; quit _ TRUE}; KbdCurSel: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {curWho _ cursel; quit _ TRUE}; KbdToPoint: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] _ Operate[viewer, toPoint]}; KbdSelect: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {nullKbd _ FALSE; [recordAtom, quit] _ Operate[viewer, selectVanilla]}; KbdSelectPendingDelete: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] _ Operate[viewer, selectPendingDelete]}; KbdDelete: PROC [viewer: Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] _ Operate[viewer, delete]}; KbdInvertPendingDelete: PROC [viewer: Viewer] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] = { Doit: PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] = { TEditSelection.Deselect[primary]; tSel.pendingDelete _ NOT tSel.pendingDelete; TEditSelection.MakeSelection[selection: primary, new: tSel]; RETURN}; quit _ TRUE; TEditInputOps.CallWithLocks[Doit, read]; RETURN}; Operate: PROC [viewer: Viewer, op: Op] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE] = { root: TiogaOps.Ref; IF viewer = NIL THEN RETURN [FALSE, FALSE]; root _ TiogaOps.ViewerDoc[viewer]; IF root = NIL THEN RETURN [FALSE, FALSE]; SelectAndOp[curWhere, curWho, op]; quit _ TRUE; }; SelectAndOp: PROC [where: Where, who: Who, op: Op] = { start, end: Location _ undef; SELECT who FROM branch, node, line, compound, word, subword, char => [start, end] _ SelectMine[where, who]; cursel => { SetPendhood[FALSE]; SELECT where FROM begin => TiogaOps.CaretBefore[]; end => TiogaOps.CaretAfter[]; all => NULL; ENDCASE => ERROR; }; ENDCASE => ERROR; SELECT op FROM toPoint => TiogaOps.CaretOnly[]; selectVanilla => NULL; selectPendingDelete => SetPendhood[TRUE]; delete => { hackCR: BOOL _ FALSE; IF who = line THEN { rIn: BOOL; rChar: CHAR; [rIn, rChar] _ GetNextChar[end]; hackCR _ rIn AND rChar = '\n; }; TiogaOps.Delete[]; IF hackCR THEN TiogaOps.DeleteNextCharacter[]; }; ENDCASE => ERROR; RETURN}; LevelProcs: TYPE = RECORD [ mayBeEmpty: BOOL, Valid: PROC [c: CHAR] RETURNS [v: BOOL], Boundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] ]; undef: Location = [NIL, -47]; levelProcses: ARRAY Level OF LevelProcs = [ branch: [TRUE, AllValid, BranchBoundary], node: [TRUE, AllValid, NodeBoundary], line: [TRUE, LineValid, LineBoundary], compound: [FALSE, CompoundChar, CompoundBoundary], word: [FALSE, AllValid, WordBoundary], subword: [FALSE, AllValid, SubwordBoundary], char: [FALSE, AllValid, CharBoundary] ]; AllValid: PROC [c: CHAR] RETURNS [v: BOOL] = {v _ TRUE}; LineValid: PROC [c: CHAR] RETURNS [v: BOOL] = {v _ c # '\n}; CompoundChar: PROC [c: CHAR] RETURNS [v: BOOL] = {v _ SELECT c FROM IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9], '. => TRUE, ENDCASE => FALSE}; AlphaNumeric: PROC [c: CHAR] RETURNS [v: BOOL] = {v _ SELECT c FROM IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9] => TRUE, ENDCASE => FALSE}; IsUpper: PROC [c: CHAR] RETURNS [v: BOOL] = {v _ c IN ['A .. 'Z]}; BranchBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { IF NOT NodeBoundary[border, loc, other] THEN RETURN [FALSE]; IF other = undef THEN RETURN [TRUE]; b _ SELECT border FROM begin => TiogaOps.LastLocWithin[loc.node] = TiogaOps.LastLocWithin[other.node], end => loc = TiogaOps.LastLocWithin[other.node], ENDCASE => ERROR; RETURN}; NodeBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { b _ SELECT border FROM begin => loc.where <= 0, end => loc.where >= TiogaOps.GetRope[loc.node].Length[], ENDCASE => ERROR; RETURN}; LineBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { b _ SELECT border FROM begin => GetPrevChar[loc].c = '\n, end => GetNextChar[loc].c = '\n, ENDCASE => ERROR; RETURN}; CompoundBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { lIn, rIn: BOOL; lC, rC: CHAR; [lIn, lC] _ GetPrevChar[loc]; [rIn, rC] _ GetNextChar[loc]; IF (NOT lIn) AND (NOT rIn) THEN ERROR; IF (NOT lIn) OR (NOT rIn) THEN RETURN [TRUE]; b _ CompoundChar[lC] # CompoundChar[rC]; RETURN}; WordBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { lIn, rIn: BOOL; lC, rC: CHAR; [lIn, lC] _ GetPrevChar[loc]; [rIn, rC] _ GetNextChar[loc]; IF (NOT lIn) AND (NOT rIn) THEN ERROR; IF (NOT lIn) OR (NOT rIn) THEN RETURN [TRUE]; IF NOT (AlphaNumeric[lC] AND AlphaNumeric[rC]) THEN RETURN [lC # rC]; b _ FALSE; RETURN}; SubwordBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { lIn, rIn, ur: BOOL; lC, rC, rrC: CHAR; [lIn, lC] _ GetPrevChar[loc]; [rIn, rC] _ GetNextChar[loc]; IF (NOT lIn) AND (NOT rIn) THEN ERROR; IF (NOT lIn) OR (NOT rIn) THEN RETURN [TRUE]; IF NOT (AlphaNumeric[lC] AND AlphaNumeric[rC]) THEN RETURN [lC # rC]; ur _ IsUpper[rC]; IF (NOT IsUpper[lC]) THEN RETURN [ur]; IF NOT ur THEN RETURN [FALSE]; rrC _ GetNextChar[NextLoc[loc]].c; IF NOT AlphaNumeric[rrC] THEN RETURN [FALSE]; b _ NOT IsUpper[rrC]; RETURN}; CharBoundary: PROC [border: Border, loc, other: Location] RETURNS [b: BOOL] = { lIn, rIn: BOOL; lC, rC: CHAR; [lIn, lC] _ GetPrevChar[loc]; [rIn, rC] _ GetNextChar[loc]; IF (NOT lIn) AND (NOT rIn) THEN ERROR; b _ TRUE; RETURN}; StandardSelection: TYPE = RECORD [ viewer: Viewer, start, end, caret: --point, not char--Location, caretBefore, pointOnly, pendingDelete: BOOL]; GetStandardSelection: PROC RETURNS [ss: StandardSelection] = { level: TiogaOps.SelectionGrain; EndAdjust: PROC = {--convert end from char pointer to point pointer IF ss.start = ss.end AND TiogaOps.GetRope[ss.start.node].Length[] = 0 THEN {IF ss.end.where # 0 THEN ERROR} ELSE ss.end _ NextLoc[ss.end]; }; startLen, endLen: INT; [ss.viewer, ss.start, ss.end, level, ss.caretBefore, ss.pendingDelete] _ TiogaOps.GetSelection[]; startLen _ TiogaOps.GetRope[ss.start.node].Length[]; endLen _ TiogaOps.GetRope[ss.end.node].Length[]; IF ss.start.where > startLen OR ss.end.where > endLen THEN { MessageWindow.Append["Tioga screwed up again", TRUE]; ss.start.where _ MIN[ss.start.where, startLen]; ss.end.where _ MIN[ss.end.where, endLen]}; SELECT level FROM point => {ss.caret _ ss.start; ss.pointOnly _ TRUE}; char, word => { EndAdjust[]; ss.caret _ IF ss.caretBefore THEN ss.start ELSE ss.end; ss.pointOnly _ ss.start = ss.end; }; node, branch => { EndAdjust[]; ss.caret _ IF ss.caretBefore THEN ss.start ELSE ss.end; ss.pointOnly _ ss.start = ss.end; }; ENDCASE => ERROR; RETURN}; SelectMine: PROC [where: Where, l: Level] RETURNS [start, end: Location] = { ss: StandardSelection _ GetStandardSelection[]; docStart, docEnd: Location; caretBefore, empty: BOOL _ FALSE; start _ end _ ss.caret; [docStart, docEnd, empty] _ GetDocLimits[ss.caret]; IF empty THEN {Bitch["EmptyDocument"]; RETURN}; SELECT where FROM begin => { WHILE NOT (end = docStart OR InLevel[l, end, -1]) DO end _ PrevLoc[end]; IF levelProcses[l].mayBeEmpty THEN EXIT; ENDLOOP; start _ IF (NOT levelProcses[l].mayBeEmpty) AND end # docStart THEN PrevLoc[end] ELSE end; WHILE NOT (start = docStart OR levelProcses[l].Boundary[begin, start, end]) DO start _ PrevLoc[start] ENDLOOP; caretBefore _ TRUE; }; end => { WHILE NOT (start = docEnd OR InLevel[l, start, 1]) DO start _ NextLoc[start]; IF levelProcses[l].mayBeEmpty THEN EXIT; ENDLOOP; end _ IF (NOT levelProcses[l].mayBeEmpty) AND start # docEnd THEN NextLoc[start] ELSE start; WHILE NOT (end = docEnd OR levelProcses[l].Boundary[end, end, start]) DO end _ NextLoc[end] ENDLOOP; }; all => SELECT ss.caretBefore FROM FALSE => { caret: Location _ ss.caret; WHILE NOT (caret = docEnd OR InLevel[l, caret, -1] OR levelProcses[l].mayBeEmpty) DO caret _ NextLoc[caret] ENDLOOP; start _ IF (NOT levelProcses[l].mayBeEmpty) AND caret # docStart THEN PrevLoc[caret] ELSE caret; WHILE NOT (start = docStart OR levelProcses[l].Boundary[begin, start, undef]) DO start _ PrevLoc[start] ENDLOOP; end _ caret; WHILE NOT (end = docEnd OR levelProcses[l].Boundary[end, end, start]) DO end _ NextLoc[end] ENDLOOP; }; TRUE => { caret: Location _ ss.caret; WHILE NOT (caret = docStart OR InLevel[l, caret, 1] OR levelProcses[l].mayBeEmpty) DO caret _ PrevLoc[caret] ENDLOOP; start _ caret; WHILE NOT (start = docStart OR levelProcses[l].Boundary[begin, start, undef]) DO start _ PrevLoc[start] ENDLOOP; end _ IF (NOT levelProcses[l].mayBeEmpty) AND caret # docEnd THEN NextLoc[caret] ELSE caret; WHILE NOT (end = docEnd OR levelProcses[l].Boundary[end, end, start]) DO end _ NextLoc[end] ENDLOOP; }; ENDCASE => ERROR; ENDCASE => ERROR; SELECT TRUE FROM start = end => TiogaOps.SelectPoint[ viewer: ss.viewer, caret: start]; start # end => TiogaOps.SetSelection[ viewer: ss.viewer, start: start, end: PrevLoc[end], level: char, caretBefore: caretBefore]; ENDCASE => ERROR; RETURN}; Bitch: PROC [msg: ROPE] = { MessageWindow.Append[message: msg, clearFirst: TRUE]; MessageWindow.Blink[]}; InLevel: PROC [l: Level, loc: Location, side: [-1 .. 1]] RETURNS [in: BOOL] = { inBounds: BOOL; char: CHAR; SELECT side FROM -1 => [inBounds, char] _ GetPrevChar[loc]; 1 => [inBounds, char] _ GetNextChar[loc]; ENDCASE => ERROR; IF NOT inBounds THEN RETURN [FALSE]; in _ levelProcses[l].Valid[char]; RETURN}; GetNextChar: PROC [loc: Location] RETURNS [inNode: BOOL, c: CHAR] = { rope: ROPE _ TiogaOps.GetRope[loc.node]; len: INT _ rope.Length[]; IF loc = [NIL, 0] THEN RETURN [FALSE, '\n]; IF loc.node = TiogaOps.Root[loc.node] THEN ERROR; IF loc.where IN [0 .. len) THEN RETURN [TRUE, rope.Fetch[loc.where]]; inNode _ FALSE; c _ '\n; }; GetPrevChar: PROC [loc: Location] RETURNS [inNode: BOOL, c: CHAR] = { rope: ROPE _ TiogaOps.GetRope[loc.node]; len: INT _ rope.Length[]; IF loc = [NIL, 0] THEN RETURN [FALSE, '\n]; IF loc.node = TiogaOps.Root[loc.node] THEN ERROR; IF loc.where IN (0 .. len] THEN RETURN [TRUE, rope.Fetch[loc.where-1]]; inNode _ FALSE; c _ '\n; }; NextLoc: PROC [loc: Location] RETURNS [next: Location] = { rope: ROPE _ TiogaOps.GetRope[loc.node]; len: INT _ rope.Length[]; IF len > loc.where THEN RETURN [[loc.node, loc.where+1]]; next _ [TiogaOps.StepForward[loc.node], 0]; RETURN}; PrevLoc: PROC [loc: Location] RETURNS [prev: Location] = { IF loc.where > 0 THEN RETURN [[loc.node, loc.where-1]]; prev _ [TiogaOps.StepBackward[loc.node], 0]; prev.where _ TiogaOps.GetRope[prev.node].Length[]; RETURN}; GetDocLimits: PROC [loc: Location] RETURNS [start, end: Location, empty: BOOL] = { root: TiogaOps.Ref _ TiogaOps.Root[loc.node]; firstNode: TiogaOps.Ref _ TiogaOps.FirstChild[root]; lastNode: TiogaOps.Ref _ TiogaOps.LastWithin[root]; start _ [firstNode, 0]; end _ [lastNode, TiogaOps.GetRope[lastNode].Length[]]; empty _ TiogaOps.LocOffset[loc1: start, loc2: end, break: 0] = 0; RETURN}; SetPendhood: PROC [pend: BOOL] = { viewer: Viewer; start, end: Location; level: TiogaOps.SelectionGrain; caretBefore, pendingDelete: BOOL; [viewer, start, end, level, caretBefore, pendingDelete] _ TiogaOps.GetSelection[]; IF pendingDelete # pend THEN TiogaOps.SetSelection[viewer, start, end, level, caretBefore, pend]; RETURN}; TrackProfile: PROC [reason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- ~ { emptyNext _ UserProfile.Boolean["KeyboardTioga.EmptyNext", TRUE]; RETURN}; Start: PROC = { UserProfile.CallWhenProfileChanges[TrackProfile]; TIPUser.RegisterTIPPredicate[$NullKbd, NullKbd]; TIPUser.RegisterTIPPredicate[$EmptyKbdNext, EmptyKbdNext]; TiogaOps.RegisterCommand[$KbdIgnore, KbdIgnore]; TiogaOps.RegisterCommand[$KbdNull, KbdNull]; TiogaOps.RegisterCommand[$KbdNotNull, KbdNotNull]; TiogaOps.RegisterCommand[$KbdBranch, KbdBranch]; TiogaOps.RegisterCommand[$KbdNode, KbdNode]; TiogaOps.RegisterCommand[$KbdLine, KbdLine]; TiogaOps.RegisterCommand[$KbdCompound, KbdCompound]; TiogaOps.RegisterCommand[$KbdWord, KbdWord]; TiogaOps.RegisterCommand[$KbdSubWord, KbdSubWord]; TiogaOps.RegisterCommand[$KbdChar, KbdChar]; TiogaOps.RegisterCommand[$KbdCurSel, KbdCurSel]; TiogaOps.RegisterCommand[$KbdBegin, KbdBegin]; TiogaOps.RegisterCommand[$KbdEnd, KbdEnd]; TiogaOps.RegisterCommand[$KbdAll, KbdAll]; TiogaOps.RegisterCommand[$KbdToPoint, KbdToPoint]; TiogaOps.RegisterCommand[$KbdSelect, KbdSelect]; TiogaOps.RegisterCommand[$KbdSelectPendingDelete, KbdSelectPendingDelete]; TiogaOps.RegisterCommand[$KbdDelete, KbdDelete]; TiogaOps.RegisterCommand[$KbdInvertPendingDelete, KbdInvertPendingDelete]; RETURN}; Start[]; }. ˜KeyboardTioga.Mesa Last Edited by: Spreitzer, December 1, 1985 12:39:35 pm PST Last tweaked by Mike Spreitzer on January 28, 1988 5:49:21 pm PST Κ*– "cedar" style˜Icode™™;K™A—K˜KšΟk œ|˜…K˜šΠbx œœ˜KšœS˜ZK˜K˜K˜Kšœœœ˜Kšœœ˜$Kšœ œ˜#K˜Kšœœ˜ Kšœœ˜#K˜Kšœœ?˜HKšœœ˜"K˜Kšœœ9˜AKšœ œ˜-K˜Kšœ œœ˜K˜K˜K˜ Kšœ œœ˜K˜š ΟnœœœœΟcœ˜9Kšœ ˜—K˜š Ÿ œœœœ œ˜>Kšœ˜—K˜šŸ œœœœœœœœ œ˜wKšœœ˜—K˜šŸœœœœœœœœ œ˜uKšœ œ˜—K˜šŸ œœœœœœœœ œ˜xKšœ œ˜—K˜šŸœœœœœœœœ œ˜vKšœœ˜ —K˜šŸœœœœœœœœ œ˜tKšœœ˜—K˜šŸœœœœœœœœ œ˜tKšœœ˜—K˜šŸ œœœœœœœœ œ˜wKšœœ˜—K˜šŸœœœœœœœœ œ˜uKšœœ˜—K˜šŸœœœœœœœœ œ˜uKšœœ˜—K˜šŸ œœœœœœœœ œ˜yKšœœ˜!—K˜šŸœœœœœœœœ œ˜uKšœœ˜—K˜šŸ œœœœœœœœ œ˜xKšœœ˜ —K˜šŸœœœœœœœœ œ˜uKšœœ˜—K˜šŸ œœœœœœœœ œ˜wKšœœ˜—K˜šŸ œœœœœœœœ œ˜xKšœ0˜0—K˜šŸ œœœœœœœœ œ˜wKšœ œ˜Kšœ5˜5—K˜šŸœœœœœœœœ œ˜„Kšœ<˜<—K˜šŸ œœœœœœœœ œ˜wKšœ/˜/—K˜šŸœœœœœœœ˜gšŸœœ8˜BKšœ!˜!Kšœœ˜,Kšœ<˜K˜šŸ œœ 0˜Cšœœ-˜EKšœœœœ˜%Kšœ˜—K˜—Kšœœ˜Kšœa˜aKšœ4˜4Kšœ0˜0šœœœ˜