DIRECTORY Ascii, Commander, CommanderOps, Convert, ForkOps, Handwriting, HelpStrings, IO, PFS, Rope, SymTab, Xl, XlCursor, XlCutBuffers, XlDB, XlTQOps, XlFontOps, XTk, XTkCommon, XTkContainers, XTkFriends, XTkLabels, XTkOps, XTkPopUps, XTkPrivate, XTkWidgets, XTkHelpShells, XTkGestureInput, X11SimulateKeyInput; X11HandwritingImpl: CEDAR MONITOR IMPORTS Ascii, Commander, CommanderOps, Convert, ForkOps, Handwriting, HelpStrings, IO, PFS, Rope, SymTab, Xl, XlCutBuffers, XlDB, XlTQOps, XlFontOps, XTk, XTkContainers, XTkFriends, XTkLabels, XTkOps, XTkPopUps, XTkPrivate, XTkWidgets, XTkHelpShells, XTkGestureInput, X11SimulateKeyInput ~ BEGIN uniStrokesTraining: Handwriting.TrainingDB; defaultTraining: Handwriting.TrainingDB; gestures: Handwriting.TrainingDB; myClass: XTk.Class ¬ XTkFriends.CreateClass[[key: $X11HWRImpl, classNameHint: $X11HWRImpl, cursorKey: NEW[XlCursor.StandardFontCursors ¬ XlCursor.StandardFontCursors.plus], backgroundKey: $white]]; CreateMine: PROC [widgetSpec: XTk.WidgetSpec ¬ []] RETURNS [XTk.Widget] = { RETURN [ XTk.CreateWidget[widgetSpec, myClass] ]; }; Stroke: TYPE = LIST OF Xl.Point; Strokes: TYPE = LIST OF Stroke; UIInstance: TYPE ~ REF UIInstanceRec; UIInstanceRec: TYPE ~ RECORD [ gridWidth: INT ¬ 10, wid: XTk.Widget ¬ NIL, label: XTk.Widget ¬ NIL, labelStyle: XTkLabels.StyleSpec ¬ [], c: Xl.Connection ¬ NIL, gc: Xl.GContext ¬ NIL, strokes: LIST OF Stroke ¬ NIL, rememberedText: Rope.ROPE ¬ NIL, currText: Rope.ROPE ¬ NIL, currInsertPos: INT ¬ 0, currFinal: BOOL ¬ TRUE, lastChar: INT ¬ -1, lastGrid: INT ¬ -1, uniStrokes: BOOL ¬ FALSE, db: Handwriting.TrainingDB ¬ NIL, regressionDB: Handwriting.TrainingDB ¬ NIL, lastRep: LIST OF Handwriting.Report ¬ NIL, firstRep: LIST OF Handwriting.Report ¬ NIL, lastTInst: TrainingInstance ¬ NIL ]; TrainingInstance: TYPE ~ REF TrainingInstanceRec; TrainingInstanceRec: TYPE ~ RECORD [ uiInst: UIInstance ¬ NIL, gc: Xl.GContext ¬ NIL, strokeMarkGC: Xl.GContext ¬ NIL, shell: XTk.Widget ¬ NIL, help: HelpStrings.Handle ¬ NIL, wid1: XTk.Widget ¬ NIL, wid2, nl2: XTk.Widget ¬ NIL, wid3, nl3: XTk.Widget ¬ NIL, regressionsName: XTk.Widget ¬ NIL, trainingName: XTk.Widget ¬ NIL, trainingChar: XTk.Widget ¬ NIL, lastNS1, lastNS2: Handwriting.Normalized ¬ NIL ]; fileIdx: CARD ¬ 0; NewFileName: ENTRY PROC [ext: Rope.ROPE] RETURNS [name: Rope.ROPE] = { name ¬ IO.PutFR["/tmp/x11handwriting-%g%g", IO.card[fileIdx], IO.rope[ext]]; fileIdx ¬ fileIdx+1; }; CleanupRope: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = { max: INT ~ 80; pos: INT; length: INT ¬ Rope.Length[r]; IF length>max THEN {r ¬ Rope.Substr[r, 0, max]; length ¬ max}; pos ¬ Rope.SkipTo[r, 0, "\r\l"]; IF pos255 THEN i ¬ ORD['?]; RETURN [VAL[i]] }; DrawCaret: XTk.WidgetNotifyProc = { ui: UIInstance ~ NARROW[registerData]; text: Rope.ROPE ¬ ui.currText; index: INT ¬ ui.currInsertPos; subText: Rope.ROPE ¬ Rope.Substr[text, 0, index]; te: Xl.TextExtentsRec ¬ Xl.QueryTextExtents[ui.c, ui.labelStyle.font, subText]; x: INT ¬ te.overallRight-te.overallLeft+ui.labelStyle.space.width; y: INT ¬ te.fontAscent+ui.labelStyle.space.height+1; Xl.DrawLine[ui.c, widget.window, [x, y], [x+10, y+14], ui.gc]; Xl.DrawLine[ui.c, widget.window, [x, y], [x-10, y+14], ui.gc]; }; Exposure: XTk.WidgetNotifyProc = { ui: UIInstance ~ NARROW[registerData]; RepaintUI[ui]; }; CharOps: XTk.WidgetNotifyProc = { ui: UIInstance ~ NARROW[registerData]; SELECT callData FROM $clear => { [] ¬ ClearText[ui]; MakeFinal[ui]; }; $bs => { MakeFinal[ui]; [] ¬ DoBackspace[ui]; MakeFinal[ui]; }; $sp => {MakeFinal[ui]; DoChar[ui, ' ]; MakeFinal[ui]}; $aAbB => {ChangeChar[ui, $aAbB]}; $xYz0 => {SecondChoice[ui, $xYz0]}; $training => {MakeTraining[ui]}; ENDCASE => {} }; XOps: XTk.WidgetNotifyProc = { ui: UIInstance ~ NARROW[registerData]; SELECT callData FROM $xSet => { text: Rope.ROPE ¬ ui.currText; XlCutBuffers.Put[ui.c, text, NIL]; X11SimulateKeyInput.Send[widget.connection, text]; }; $xSetP => { text: Rope.ROPE ¬ Rope.Concat[ui.currText, "\n"]; XlCutBuffers.Put[ui.c, text, NIL]; X11SimulateKeyInput.Send[widget.connection, text]; }; $xSetCR => { X11SimulateKeyInput.Send[widget.connection, "\n"]; }; $xSetBS => { X11SimulateKeyInput.Send[widget.connection, "\b"]; }; ENDCASE => {} }; ROps: XTk.WidgetNotifyProc = { Send: PROC [ui: UIInstance, code: INT] = { a: Xl.XAtom ¬ Xl.MakeAtom[ui.c, "PARC_Handwriting"]; w: Xl.Window ¬ Xl.GetInputFocus[ui.c].window; Xl.SendClientMessage32[c: ui.c, destination: [[1]], window: w, type: a, data: [Xl.AtomId[a], LOOPHOLE[code], 0, 0, 0]]; }; ui: UIInstance ~ NARROW[registerData]; SELECT callData FROM $rSet => { stuffKey: CARD32 ~ LOOPHOLE[-1]; XlCutBuffers.Put[ui.c, ui.currText, NIL]; Send[ui, -1] }; $rSetP => { stuffKey: CARD32 ~ LOOPHOLE[-1]; XlCutBuffers.Put[ui.c, Rope.Concat[ui.currText, "\n"], NIL]; Send[ui, -1] }; $rClear => { Send[ui, ORD[Ascii.DEL]]; }; $rBs => { Send[ui, ORD[Ascii.BS]]; }; $rEnter => { XlCutBuffers.Put[ui.c, "\r", NIL]; Send[ui, -2]; }; $rNext => { Send[ui, -3]; }; $from => { x: Rope.ROPE ¬ XlCutBuffers.Get[ui.c]; x ¬ CleanupRope[x]; [] ¬ ClearText[ui]; MakeFinal[ui]; ui.currText ¬ x; ui.currInsertPos ¬ Rope.Length[x]; Push[ui]; }; ENDCASE => {} }; ShowNormalizedStrokes: PROC [wid: XTk.Widget, gc: Xl.GContext, ns: REF, strokeMarkGC: Xl.GContext ¬ NIL] = { IF ns#NIL THEN { mx: INT ¬ wid.actual.size.width/2; my: INT ¬ wid.actual.size.height/2; strokes: Strokes ¬ Handwriting.UnNormalize[ns, MIN[mx, my]]; Xl.ClearArea[wid.connection, wid.window, [0, 0], [4000, 4000]]; DrawStrokes[wid.connection, wid.window, gc, strokes, [mx, my]]; IF strokeMarkGC#NIL THEN { idx: INT ¬ CountStrokes[strokes]; FOR l: Strokes ¬ strokes, l.rest WHILE l#NIL DO stroke: LIST OF Xl.Point ¬ l.first; IF stroke#NIL THEN DrawStrokeMark[wid.connection, wid.window, strokeMarkGC, stroke.first, [mx, my], idx]; idx ¬ idx - 1; ENDLOOP; }; }; }; CountStrokes: PROC [strokes: Strokes] RETURNS [cnt: INT ¬ 0] = { FOR ss: Strokes ¬ strokes, ss.rest WHILE ss#NIL DO cnt ¬ cnt+1; ENDLOOP; }; DrawStrokeMark: PROC [c: Xl.Connection, d: Xl.Drawable, gc: Xl.GContext, p, offset: Xl.Point, idx: INT] = { p1, p2: Xl.Point; p ¬ p1 ¬ p2 ¬ [p.x+offset.x, p.y+offset.y]; Xl.DrawLine[c, d, [p.x-2, p.y-2], [p.x+2, p.y-2], gc]; --top Xl.DrawLine[c, d, [p.x+2, p.y-2+1], [p.x+2, p.y+2], gc]; --right Xl.DrawLine[c, d, [p.x+2-1, p.y+2], [p.x-2, p.y+2], gc]; --bottom Xl.DrawLine[c, d, [p.x-2, p.y+2-1], [p.x-2, p.y-2+1], gc]; --left FOR i: INT IN [0..idx) DO Xl.DrawLine[c, d, [p1.x-5, p1.y-2], [p1.x-2-1, p1.y-2], gc]; p1.y ¬ p1.y + 2 ENDLOOP; FOR i: INT IN [0..idx) DO Xl.DrawLine[c, d, [p2.x-2, p2.y-5], [p2.x-2, p2.y-2-1], gc]; p2.x ¬ p2.x + 2 ENDLOOP; }; RegressionOps: XTk.WidgetNotifyProc = { ENABLE { IO.Error => { ti: TrainingInstance ~ NARROW[registerData]; HelpStrings.MakeVisible[ti.help]; HelpStrings.Clear[ti.help]; HelpStrings.Display[ti.help, msg]; GOTO Oops }; PFS.Error => { ti: TrainingInstance ~ NARROW[registerData]; HelpStrings.MakeVisible[ti.help]; HelpStrings.Clear[ti.help]; HelpStrings.Display[ti.help, error.explanation]; GOTO Oops }; }; ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ~ ti.uiInst; SELECT callData FROM $rememberReg => { strokes: Strokes ¬ ui.strokes; IF strokes#NIL THEN { ui.regressionDB ¬ Handwriting.TrainDB[ui.regressionDB, strokes, ui.lastChar]; }; }; $clearReg => { ui.regressionDB ¬ NIL; }; $saveReg => { regressionsName: Rope.ROPE ¬ NewFileName[".regressions"]; regressionDB: Handwriting.TrainingDB ¬ ui.regressionDB; IF regressionDB#NIL THEN { XTkWidgets.SetText[ui.label, Rope.Concat["writing ", regressionsName]]; Handwriting.WriteDB[regressionsName, regressionDB]; XTkWidgets.SetText[ui.label, Rope.Concat["--> ", regressionsName]]; }; }; $loadReg => { regressionsName: Rope.ROPE ¬ XTkWidgets.GetText[ti.regressionsName]; db: Handwriting.TrainingDB; db ¬ Handwriting.ReadDB[regressionsName ! IO.Error, PFS.Error => {db ¬ NIL; CONTINUE}]; ui.regressionDB ¬ db }; $fake => { regressionDB: Handwriting.TrainingDB ¬ ui.regressionDB; IF regressionDB#NIL THEN { ns: Handwriting.Normalized ¬ Handwriting.NextNormalized[regressionDB, NIL]; ui.regressionDB ¬ Handwriting.UnTrainDB[regressionDB, ns]; FakeStrokes[ui, Handwriting.UnNormalize[ns, 40]]; }; }; ENDCASE => {}; EXITS Oops => {}; }; Train: XTk.WidgetNotifyProc = { ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ~ ti.uiInst; name: Rope.ROPE ¬ XTkWidgets.GetText[ti.trainingChar]; length: INT ¬ Rope.Length[name]; strokes: Strokes ¬ ui.strokes; IF length#1 THEN { XTkWidgets.SetText[ui.label, "failed: define a single character"]; RETURN; }; IF strokes=NIL THEN { XTkWidgets.SetText[ui.label, "failed: input strokes first"]; RETURN; }; BEGIN ch: CHAR ~ Rope.Fetch[name, 0]; db: Handwriting.TrainingDB ¬ GetModifiableDB[ui]; ui.db ¬ Handwriting.TrainDB[db, strokes, ORD[ch]]; END; }; GetModifiableDB: PROC [ui: UIInstance] RETURNS [db: Handwriting.TrainingDB] = { db ¬ ui.db; IF db=uniStrokesTraining OR db=defaultTraining OR db=gestures THEN db ¬ Handwriting.MergeDB[db: db]; }; Forget: XTk.WidgetNotifyProc = { ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ~ ti.uiInst; ns: Handwriting.Normalized ¬ NIL; SELECT callData FROM $r1 => ns ¬ ti.lastNS1; $r2 => ns ¬ ti.lastNS2; ENDCASE => {}; IF ns#NIL AND ui.db#NIL THEN { db: Handwriting.TrainingDB ¬ GetModifiableDB[ui]; ui.db ¬ Handwriting.UnTrainDB[db, ns]; }; }; SpecialReadTraining: PROC [name: Rope.ROPE] RETURNS [db: Handwriting.TrainingDB ¬ NIL] = { db ¬ Handwriting.ReadDB[name ! IO.Error, PFS.Error => {db ¬ NIL; CONTINUE}]; IF db=NIL THEN db ¬ Handwriting.ReadDB[Rope.Concat["/Cedar/X11Handwriting/", name] ! IO.Error, PFS.Error => {db ¬ NIL; CONTINUE}]; }; ReadDefaultTraining: PROC [x: REF ANY] = { g, d, u: Handwriting.TrainingDB ¬ NIL; g ¬ SpecialReadTraining["gestures.hwrTraining"]; IF g#NIL THEN gestures ¬ g; d ¬ SpecialReadTraining["default.hwrTraining"]; IF d#NIL THEN defaultTraining ¬ d; u ¬ SpecialReadTraining["unistrokes.hwrTraining"]; IF u#NIL THEN uniStrokesTraining ¬ u; }; DefaultTraining: PROC [ui: UIInstance] = { db: Handwriting.TrainingDB ¬ defaultTraining; IF db=NIL THEN db ¬ SpecialReadTraining["default.hwrTraining"]; IF db#NIL THEN { ui.db ¬ db; ui.uniStrokes ¬ FALSE }; }; UnistrokesTraining: PROC [ui: UIInstance] = { db: Handwriting.TrainingDB ¬ uniStrokesTraining; IF db=NIL THEN db ¬ SpecialReadTraining["unistrokes.hwrTraining"]; IF db#NIL THEN { ui.db ¬ db; ui.uniStrokes ¬ TRUE }; }; ReadTrainingDB: XTk.WidgetNotifyProc = { ENABLE { IO.Error => { ti: TrainingInstance ~ NARROW[registerData]; HelpStrings.MakeVisible[ti.help]; HelpStrings.Clear[ti.help]; HelpStrings.Display[ti.help, msg]; GOTO Oops }; PFS.Error => { ti: TrainingInstance ~ NARROW[registerData]; HelpStrings.MakeVisible[ti.help]; HelpStrings.Clear[ti.help]; HelpStrings.Display[ti.help, error.explanation]; GOTO Oops }; }; ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ~ ti.uiInst; name: Rope.ROPE ¬ XTkWidgets.GetText[ti.trainingName]; db: Handwriting.TrainingDB ¬ Handwriting.ReadDB[name]; IF db#NIL THEN ui.db ¬ Handwriting.MergeDB[into: db, db: ui.db]; EXITS Oops => {}; }; ClearTrainingDB: XTk.WidgetNotifyProc = { ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ~ ti.uiInst; ui.db ¬ NIL; }; WriteTrainingDB: XTk.WidgetNotifyProc = { ENABLE { IO.Error => { ti: TrainingInstance ~ NARROW[registerData]; HelpStrings.MakeVisible[ti.help]; HelpStrings.Clear[ti.help]; HelpStrings.Display[ti.help, msg]; GOTO Oops }; PFS.Error => { ti: TrainingInstance ~ NARROW[registerData]; HelpStrings.MakeVisible[ti.help]; HelpStrings.Clear[ti.help]; HelpStrings.Display[ti.help, error.explanation]; GOTO Oops }; }; ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ~ ti.uiInst; name: Rope.ROPE ¬ NewFileName[".training"]; db: Handwriting.TrainingDB ¬ ui.db; IF db#NIL THEN { XTkWidgets.SetText[ui.label, Rope.Concat["writing ", name]]; Handwriting.WriteDB[name, db]; XTkWidgets.SetText[ui.label, Rope.Concat["--> ", name]]; }; EXITS Oops => {}; }; Recognize: PROC [ui: UIInstance, strokes: Strokes] = { reps: LIST OF Handwriting.Report; ti: TrainingInstance ¬ ui.lastTInst; db: Handwriting.TrainingDB ¬ ui.db; ns: Handwriting.Normalized ¬ Handwriting.Normalize[strokes]; IF db=NIL THEN { XTkWidgets.SetText[ui.label, "read a training database first"]; RETURN; }; reps ¬ ui.lastRep ¬ ui.firstRep ¬ Handwriting.RecognizeNormalized[db, ns]; IF reps#NIL THEN DoChar[ui, ToChar[reps.first.ch]]; IF ti#NIL THEN Forward[ui, ti, ns]; }; Forward: PROC [ui: UIInstance, ti: TrainingInstance, ns: Handwriting.Normalized] = { IF ti#NIL THEN { c: Xl.Connection ¬ ti.wid1.connection; reps: LIST OF Handwriting.Report ¬ ui.lastRep; ShowNormalizedStrokes[ti.wid1, ti.gc, ns, ti.strokeMarkGC]; IF reps#NIL THEN { FancyText: PROC [x: Handwriting.Report] RETURNS [r: Rope.ROPE] = { IF x.ch>32 AND x.ch<128 THEN r ¬ IO.PutFR["%g : %g", IO.int[ABS[x.badness/1000]], IO.rope[Rope.FromChar[VAL[x.ch]]]] ELSE r ¬ IO.PutFR["%g %g", IO.int[ABS[x.badness/1000]], IO.int[x.ch]]; }; ti.lastNS1 ¬ reps.first.x; ti.lastNS2 ¬ NIL; ShowNormalizedStrokes[ti.wid2, ti.gc, reps.first.x, ti.strokeMarkGC]; XTkLabels.SetText[ti.nl2, FancyText[reps.first]]; IF reps.rest#NIL THEN { ShowNormalizedStrokes[ti.wid3, ti.gc, reps.rest.first.x, ti.strokeMarkGC]; XTkLabels.SetText[ti.nl3, FancyText[reps.rest.first]]; ti.lastNS2 ¬ reps.rest.first.x; }; }; IF ui.c#c THEN Xl.Flush[c]; }; }; MakeFinal: PROC [ui: UIInstance] = { ui.currFinal ¬ TRUE; }; MakeSpecialGC: PROC [screen: Xl.Screen] RETURNS [gc: Xl.GContext] = { c: Xl.Connection ¬ screen.connection; gc ¬ Xl.MakeGContext[c, screen.root.drawable]; Xl.SetGCFunction[gc: gc, function: copy]; Xl.SetGCForeground[gc: gc, foreground: screen.blackPixel]; Xl.SetGCBackground[gc: gc, background: screen.whitePixel]; Xl.SetGCLineWidth[gc: gc, width: 0]; }; UIBindScreen: XTk.WidgetNotifyProc = { style: XTkCommon.StyleSpec ¬ []; fontName: Rope.ROPE; font: Xl.Font; ui: UIInstance ~ NARROW[registerData]; sd: Xl.ScreenDepth ~ ui.wid.screenDepth; ui.c ¬ sd.screen.connection; ui.gc ¬ XTkGestureInput.TrustedSharedGC[sd]; fontName ¬ XlDB.QueryStandardDB[ui.c, "(X11Handwriting)(FeedbackFont)"]; IF Rope.IsEmpty[fontName] THEN fontName ¬ "12x24"; font ¬ Xl.OpenFont[c: sd.screen.connection, name: fontName, details: XTkPrivate.detailsForSynchronous ! Xl.XError => { font ¬ XlFontOps.GetDefaultFont[ui.c]; CONTINUE }]; style.space.width ¬ 4; style.space.height ¬ 6; style.font ¬ font; ui.labelStyle ¬ style; XTkLabels.SetStyleSpec[ui.label, style, dont]; BEGIN vendor, heightRope: Rope.ROPE ¬ NIL; h: CARD ¬ 0; heightRope ¬ XlDB.QueryStandardDB[ui.c, "(X11Handwriting)(ActiveHeight)"]; vendor ¬ Xl.Info[ui.c].vendor; IF ~Rope.IsEmpty[heightRope] THEN h ¬ Convert.CardFromRope[heightRope ! Convert.Error => CONTINUE]; SELECT TRUE FROM h>10 AND h<400 => ui.wid.s.geometry.size.height ¬ h; Rope.Equal[vendor, "Xerox Split MPAD"] => ui.wid.s.geometry.size.height ¬ 60; ENDCASE => ui.wid.s.geometry.size.height ¬ 120; END; }; UIForgetScreen: XTk.WidgetNotifyProc = { ui: UIInstance ~ NARROW[registerData]; ti: TrainingInstance ~ SwapTrainingInstance[ui, NIL]; IF ti#NIL THEN DestroyTI[ti]; }; TIBindScreen: XTk.WidgetNotifyProc = { ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ¬ ti.uiInst; sd: Xl.ScreenDepth ~ ti.wid1.screenDepth; ti.gc ¬ XTkGestureInput.TrustedSharedGC[sd]; ti.strokeMarkGC ¬ MakeSpecialGC[sd.screen]; IF ui#NIL THEN { old: TrainingInstance ¬ SwapTrainingInstance[ui, ti]; IF old#ti THEN DestroyTI[old]; }; }; TIPostWindowCreation: XTk.WidgetNotifyProc = { ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ¬ ti.uiInst; IF ui#NIL THEN { XlTQOps.EnqueueSoon[100, ui.wid.rootTQ, DelayedTIPostWindowCreation, ti]; }; }; DelayedTIPostWindowCreation: Xl.EventProcType = { ti: TrainingInstance ~ NARROW[clientData]; ui: UIInstance ¬ ti.uiInst; IF ui#NIL AND ti.wid1.fastAccessAllowed=ok AND ti.wid2.fastAccessAllowed=ok AND ti.wid3.fastAccessAllowed=ok AND ti.nl2.fastAccessAllowed=ok AND ti.nl3.fastAccessAllowed=ok THEN { strokes: Strokes ¬ ui.strokes; IF strokes#NIL THEN { Forward[ui, ti, Handwriting.Normalize[strokes] ! Xl.XError => CONTINUE]; }; }; }; TIForgetScreen: XTk.WidgetNotifyProc = { NilTrainingInstance: ENTRY PROC [ui: UIInstance, expect: TrainingInstance] = { IF ui#NIL AND ui.lastTInst=expect THEN {ui.lastTInst ¬ NIL}; }; ti: TrainingInstance ~ NARROW[registerData]; ui: UIInstance ¬ ti.uiInst; IF ui#NIL THEN NilTrainingInstance[ui, ti]; }; PaintGrid: PROC [ui: UIInstance] = { w: INT ¬ ui.wid.actual.size.width; h: INT ¬ ui.wid.actual.size.height; gridSize: INT ¬ ui.gridWidth ¬ MAX[h/5*4, 10]; gridPos: INT ¬ gridSize; WHILE gridPos0 THEN { ui.currText ¬ Rope.Cat[Rope.Substr[text, 0, ip-1], Rope.FromChar[new], Rope.Substr[text, ip]]; }; }; LastChar: PROC [ui: UIInstance] RETURNS [ch: CHAR ¬ 0C] = { text: Rope.ROPE ~ ui.currText; ip: INT ¬ MIN[ui.currInsertPos, Rope.Length[text]]; IF ip>0 THEN { ch ¬ Rope.Fetch[text, ip-1]; }; }; DoBackspace: PROC [ui: UIInstance] RETURNS [propagate: BOOL ¬ FALSE] = { text: Rope.ROPE ~ ui.currText; ip: INT ¬ MIN[ui.currInsertPos, Rope.Length[text]]; IF ip>0 THEN { ui.currText ¬ Rope.Concat[Rope.Substr[text, 0, ip-1], Rope.Substr[text, ip]]; ui.currInsertPos ¬ ip-1; Push[ui]; } ELSE RETURN [Rope.Length[text]=0]; }; SecondChoice: PROC [ui: UIInstance, a: ATOM] = { IF ui.lastRep#NIL THEN { ui.lastRep ¬ ui.lastRep.rest; IF ui.lastRep=NIL THEN ui.lastRep ¬ ui.firstRep; IF ui.lastRep#NIL THEN { char: CHAR ¬ ToChar[ui.lastRep.first.ch]; SwapChar[ui, char]; }; }; ui.currFinal ¬ TRUE; Push[ui]; }; ChangeChar: PROC [ui: UIInstance, a: ATOM] = { char: CHAR ¬ LastChar[ui]; SELECT a FROM $aAbB => { SELECT char FROM IN ['A..'Z] => char ¬ Ascii.Lower[char]; IN ['a..'z] => char ¬ Ascii.Upper[char]; '( => char ¬ '[; '[ => char ¬ '{; '{ => char ¬ '<; '< => char ¬ '(; ') => char ¬ ']; '] => char ¬ '}; '} => char ¬ '>; '> => char ¬ '); '. => char ¬ ',; ', => char ¬ ';; '; => char ¬ ':; ': => char ¬ '.; '" => char ¬ '=; '= => char ¬ '~; '~ => char ¬ '"; '$ => char ¬ '&; '& => char ¬ '@; '@ => char ¬ '$; '/ => char ¬ '|; '| => char ¬ '\\; '\\ => char ¬ '/; '- => char ¬ '_; '_ => char ¬ '-; '+ => char ¬ '*; '* => char ¬ '+; '1 => char ¬ '7; '7 => char ¬ '1; '9 => char ¬ 'g; '5 => char ¬ 'S; '2 => char ¬ 'Z; IN [Ascii.ControlA..Ascii.ControlZ] => char ¬ char + Ascii.controlOffset - Ascii.caseOffset; ENDCASE => {}; }; $A => char ¬ Ascii.Upper[char]; $a => char ¬ Ascii.Lower[char]; $ctrl => { SELECT char FROM IN ['A..'Z] => char ¬ char - Ascii.controlOffset; IN ['a..'z] => char ¬ char - Ascii.caseOffset - Ascii.controlOffset; IN [Ascii.ControlA..Ascii.ControlZ] => char ¬ char + Ascii.controlOffset + Ascii.caseOffset; ENDCASE; }; ENDCASE => {}; SwapChar[ui, char]; ui.currFinal ¬ TRUE; Push[ui]; }; DoChar: PROC [ui: UIInstance, ch: CHAR] = { text: Rope.ROPE ¬ ui.currText; ip: INT ¬ Rope.Length[text]; IF ~ui.currFinal AND ip>0 THEN SwapChar[ui, ch] ELSE InsertRope[ui, Rope.FromChar[ch]]; ui.currFinal ¬ FALSE; ui.lastChar ¬ ORD[ch]; Push[ui]; }; SetCaretPosition: PROC [ui: UIInstance, x: INT] = { i: INT ¬ XlFontOps.QueryPosInfo[ui.c, ui.labelStyle.font, ui.currText, x-ui.labelStyle.space.width].gapIndex; ui.currInsertPos ¬ i; Push[ui]; }; SetCaretPositionAfter: PROC [ui: UIInstance, x: INT] = { i: INT ¬ XlFontOps.QueryPosInfo[ui.c, ui.labelStyle.font, ui.currText, x-ui.labelStyle.space.width].charIndex; ui.currInsertPos ¬ i+1; }; Delete: PROC [ui: UIInstance, minx, maxx: INT, remember: BOOL ¬ FALSE] = { text: Rope.ROPE ~ ui.currText; length: INT ¬ Rope.Length[text]; p1: XlFontOps.PosInfo ¬ XlFontOps.QueryPosInfo[ui.c, ui.labelStyle.font, ui.currText, minx-ui.labelStyle.space.width]; p2: XlFontOps.PosInfo ¬ XlFontOps.QueryPosInfo[ui.c, ui.labelStyle.font, ui.currText, maxx-ui.labelStyle.space.width]; startIdx: INT ¬ MIN[p1.gapIndex, length]; stopIdx: INT ¬ MIN[p2.gapIndex, length]; IF startIdx>=stopIdx THEN { startIdx ¬ MIN[p1.charIndex, length]; stopIdx ¬ MIN[startIdx+1, length]; }; ui.currText ¬ Rope.Concat[Rope.Substr[text, 0, startIdx], Rope.Substr[text, stopIdx]]; ui.currInsertPos ¬ startIdx; Push[ui]; IF remember THEN ui.rememberedText ¬ Rope.Substr[text, startIdx, stopIdx-startIdx]; }; LabelGesture: XTkGestureInput.GestureReportProc = { ui: UIInstance ~ NARROW[d1]; min, max: Xl.Point; [min, max] ¬ StrokeBounds[stroke]; IF max.x-min.x<=3 AND max.y-min.y<=3 THEN { SetCaretPosition[ui, (min.x+max.x)/2]; } ELSE { report: LIST OF Handwriting.Report ¬ Handwriting.Recognize[gestures, LIST[stroke]]; IF report#NIL THEN { SELECT report.first.ch FROM ORD['1] => SetCaretPosition[ui, (min.x+max.x)/2]; ORD['2] => Delete[ui, min.x, max.x, FALSE]; ORD['3] => { SetCaretPositionAfter[ui, (min.x+max.x)/2]; ChangeChar[ui, $aAbB]; }; ORD['4] => { x: Rope.ROPE ¬ XlCutBuffers.Get[ui.c]; x ¬ CleanupRope[x]; InsertRope[ui, x]; Push[ui]; }; ORD['5] => { x: Rope.ROPE ¬ ui.rememberedText; SetCaretPosition[ui, (min.x+max.x)/2]; InsertRope[ui, x]; Push[ui]; }; ORD['6] => Delete[ui, min.x, max.x, TRUE]; ORD['7] => { SetCaretPositionAfter[ui, (min.x+max.x)/2]; ChangeChar[ui, $ctrl]; }; ENDCASE => {}; }; }; }; StartX: PROC [list: LIST OF Xl.Point] RETURNS [x: INT ¬ 0] = { IF list=NIL THEN RETURN; FOR l: LIST OF Xl.Point ¬ list, l.rest DO IF l.rest=NIL THEN RETURN [l.first.x]; ENDLOOP }; FakeStrokes: PROC [ui: UIInstance, strokes: Strokes] = { MakeFinal[ui]; ui.strokes ¬ strokes; ui.lastGrid ¬ -1; Recognize[ui, strokes]; }; MainGesture: XTkGestureInput.GestureReportProc = { IF stroke#NIL THEN { ui: UIInstance ~ NARROW[d1]; strokes: Strokes ¬ ui.strokes; ngi: INT _ StartX[stroke]/MAX[ui.gridWidth, 10]; IF ngi#ui.lastGrid OR ui.uniStrokes THEN { MakeFinal[ui]; ui.lastGrid ¬ ngi; ui.strokes ¬ strokes ¬ LIST[stroke]; RepaintUI[ui]; } ELSE { strokes ¬ CONS[stroke, strokes]; }; ui.strokes ¬ strokes; Recognize[ui, strokes]; }; }; HWRCommand: Commander.CommandProc ~ { ui: UIInstance ~ NEW[UIInstanceRec]; menu: XTkWidgets.Widget ¬ XTkPopUps.CreateSimplePopUpButton[text: "Menu ", list: LIST[ ["help", $help, NIL, "open documentation", HelpHit], ["pseudo keyboard", NIL, NIL, "create a pseudo keyboard (alpha)", CreateAlphaKeyboard], ["numeric keyboard", NIL, NIL, "create a pseudo keyboard (numeric)", CreateSpecialKeyboard], [NIL, NIL, NEW[XTkPopUps.WidgetCreateClosureRec ¬ [TrainingSelection, ui]], NIL, DBSelectorHit], ["do training", $training, NIL, "create training ui widget", CharOps] ], defaultNotify: NIL, registerData: ui ]; aAbB: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "cap", hitProc: CharOps, registerData: ui, callData: $aAbB]; secondChoice: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "2nd", hitProc: CharOps, registerData: ui, callData: $xYz0]; sp: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "space ", hitProc: CharOps, registerData: ui, callData: $sp]; xSet: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "Xset", hitProc: XOps, registerData: ui, callData: $xSet]; xSetCR: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "Xcr", hitProc: XOps, registerData: ui, callData: $xSetCR]; xSetBS: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "Xbs", hitProc: XOps, registerData: ui, callData: $xSetBS]; controlRow: XTkWidgets.Widget ¬ XTkWidgets.CreateXStack[stack: LIST[menu, <> aAbB, secondChoice, sp, xSet, xSetCR, xSetBS]]; rSet: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "SET", hitProc: ROps, registerData: ui, callData: $rSet]; rSetP: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "SET+", hitProc: ROps, registerData: ui, callData: $rSetP]; rClear: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "r-clear", hitProc: ROps, registerData: ui, callData: $rClear]; rBs: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "r-back", hitProc: ROps, registerData: ui, callData: $rBs]; rEnter: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "enter", hitProc: ROps, registerData: ui, callData: $rEnter]; rNext: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "next", hitProc: ROps, registerData: ui, callData: $rNext]; rControlRow: XTkWidgets.Widget ¬ XTkWidgets.CreateXStack[stack: LIST[rSet, rSetP, rClear, rBs, rEnter, rNext<<, from>>]]; label: XTkWidgets.Widget ¬ ui.label ¬ XTkWidgets.CreateLabel[]; wid: XTkWidgets.Widget ¬ ui.wid ¬ CreateMine[ widgetSpec: [geometry: [size: [300, 120], borderWidth: 1]] ]; container: XTkWidgets.Widget ¬ XTkWidgets.CreateYStack[stack: LIST[label, wid, controlRow, rControlRow]]; tq: Xl.TQ ¬ Xl.CreateTQ[order: XTk.rootLockingOrder]; shell: XTkWidgets.Widget ¬ XTkWidgets.CreateShell[child: container, windowHeader: "Handwriting recognition", rootTQ: tq]; XTkGestureInput.SetReport[wid, MainGesture, ui, NIL, tq]; XTkGestureInput.SetPainting[wid, TRUE]; XTkGestureInput.SetReport[label, LabelGesture, ui]; XTkGestureInput.SetPainting[label, TRUE]; XTk.RegisterNotifier[wid, XTk.bindScreenLRKey, UIBindScreen, ui]; XTk.RegisterNotifier[wid, XTk.forgetScreenLRKey, UIForgetScreen, ui]; XTk.RegisterNotifier[label, $LabelRepaint, DrawCaret, ui]; XTkOps.RegisterNotifierProcOnEvents[widget: wid, handles: Xl.CreateEventFilter[expose], proc: Exposure, registerData: ui, generate: [exposure: TRUE]]; shell.attributes.eventMask ¬ Xl.ORSetOfEvents[shell.attributes.eventMask, [buttonRelease: TRUE, buttonPress: TRUE]]; XTkContainers.SetVaryingSize[wid, TRUE]; XTkWidgets.RealizeShell[shell]; InitializeWithDefaultTraining[ui]; }; InitializeWithDefaultTraining: PROC [x: REF ANY] = { ui: UIInstance ~ NARROW[x]; IF ui.db=NIL THEN { ui.db ¬ defaultTraining; IF ui.db=NIL THEN ForkOps.ForkDelayed[300, InitializeWithDefaultTraining, ui]; }; }; HelpHit: XTk.WidgetNotifyProc = { serverName: Rope.ROPE ~ Xl.ServerName[widget.connection]; [] ¬ CommanderOps.DoCommandRope[Rope.Cat["X11 -server ", serverName, " -- XOpen /Cedar10.1/X11Handwriting/X11HandwritingDoc.tioga"], NIL, NIL]; }; DBSelectorHit: XTk.WidgetNotifyProc = { failure: BOOL ¬ FALSE; ui: UIInstance ~ NARROW[registerData]; XTkWidgets.SetText[ui.label, "...wait"]; WITH callData SELECT FROM a: ATOM => { SELECT callData FROM $m => DefaultTraining[ui]; $u => UnistrokesTraining[ui]; ENDCASE => {}; }; lr: LIST OF Rope.ROPE => { db: Handwriting.TrainingDB ¬ NIL; FOR list: LIST OF Rope.ROPE ¬ lr, list.rest WHILE list#NIL DO IF Rope.Equal[list.first, "default", FALSE] THEN db ¬ Handwriting.MergeDB[db, defaultTraining] ELSE { d: Handwriting.TrainingDB ¬ NIL; d ¬ Handwriting.ReadDB[list.first ! IO.Error, PFS.Error => { failure ¬ TRUE; XTkWidgets.SetText[ui.label, "file failed"]; d ¬ NIL; CONTINUE }]; IF db=NIL THEN db ¬ d ELSE db ¬ Handwriting.MergeDB[db, d]; }; ENDLOOP; IF db#NIL THEN {ui.db ¬ db; ui.uniStrokes ¬ FALSE}; }; ENDCASE => {}; IF failure THEN XTkWidgets.SetText[ui.label, "partial replacement of training database"] ELSE XTkWidgets.SetText[ui.label, "training database replaced"]; IF ui.label.state=realized THEN RepaintUI[ui]; }; DestroyTI: PROC [ti: TrainingInstance] ~ { IF ti#NIL THEN XTkWidgets.DestroyShell[ti.shell] }; MakeTraining: PROC [ui: UIInstance] ~ { ti: TrainingInstance ~ NEW[TrainingInstanceRec ¬ [uiInst: ui]]; fake: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "REPLAY", hitProc: RegressionOps, registerData: ti, callData: $fake]; rememberReg: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "remember", hitProc: RegressionOps, registerData: ti, callData: $rememberReg]; clearReg: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "new", hitProc: RegressionOps, registerData: ti, callData: $clearReg]; saveReg: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "write", hitProc: RegressionOps, registerData: ti, callData: $saveReg]; loadReg: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "read", hitProc: RegressionOps, registerData: ti, callData: $loadReg]; nameReg: XTkWidgets.Widget ¬ ti.regressionsName ¬ XTkWidgets.CreateLabeledField[ label: "file:", init: "junk.hwrRegressions" ]; regressionRow: XTkWidgets.Widget ¬ XTkWidgets.CreateXStack[stack: LIST[fake, rememberReg, saveReg, clearReg, loadReg, nameReg]]; trainingChar: XTkWidgets.Widget ¬ ti.trainingChar ¬ XTkWidgets.CreateLabeledField[ label: " char:", init: "???" ]; train0: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "TRAIN", hitProc: Train, registerData: ti]; forget1: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "forget1", hitProc: Forget, registerData: ti, callData: $r1]; forget2: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "forget2", hitProc: Forget, registerData: ti, callData: $r2]; trainingRow: XTkWidgets.Widget ¬ XTkWidgets.CreateXStack[stack: LIST[train0, forget1, forget2, trainingChar]]; dbLab: XTkWidgets.Widget ¬ XTkWidgets.CreateLabel[text: "DB"]; readTraining: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "merge", hitProc: ReadTrainingDB, registerData: ti]; writeTraining: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "write", hitProc: WriteTrainingDB, registerData: ti]; clearTraining: XTkWidgets.Widget ¬ XTkWidgets.CreateButton[text: "new", hitProc: ClearTrainingDB, registerData: ti]; trainingName: XTkWidgets.Widget ¬ ti.trainingName ¬ XTkWidgets.CreateLabeledField[ label: "file:", init: "junk.hwrTraining" ]; trainingDbRow: XTkWidgets.Widget ¬ XTkWidgets.CreateXStack[stack: LIST[dbLab, writeTraining, clearTraining, readTraining, trainingName]]; wid1: XTkWidgets.Widget ¬ ti.wid1 ¬ XTkWidgets.CreateBareWidget[ widgetSpec: [geometry: [size: [120, 144], borderWidth: 1]] ]; wid2: XTkWidgets.Widget ¬ ti.wid2 ¬ XTkWidgets.CreateBareWidget[ widgetSpec: [geometry: [size: [120, 144], borderWidth: 1]] ]; wid3: XTkWidgets.Widget ¬ ti.wid3 ¬ XTkWidgets.CreateBareWidget[ widgetSpec: [geometry: [size: [120, 144], borderWidth: 1]] ]; nl1: XTkWidgets.Widget ¬ XTkWidgets.CreateLabel[text: "normalized"]; nl2: XTkWidgets.Widget ¬ ti.nl2 ¬ XTkWidgets.CreateLabel[text: " "]; nl3: XTkWidgets.Widget ¬ ti.nl3 ¬ XTkWidgets.CreateLabel[text: " "]; c1: XTkWidgets.Widget ¬ XTkWidgets.CreateYStack[stack: LIST[nl1, wid1]]; c2: XTkWidgets.Widget ¬ XTkWidgets.CreateYStack[stack: LIST[nl2, wid2]]; c3: XTkWidgets.Widget ¬ XTkWidgets.CreateYStack[stack: LIST[nl3, wid3]]; cc: XTkWidgets.Widget ¬ XTkWidgets.CreateXStack[stack: LIST[c1, c2, c3]]; container: XTkWidgets.Widget ¬ XTkWidgets.CreateYStack[stack: LIST[regressionRow, trainingDbRow, trainingRow, cc]]; shell: XTkWidgets.Widget ¬ ti.shell ¬ XTkWidgets.CreateShell[child: container, windowHeader: "Handwriting recognition training"]; XTk.RegisterNotifier[wid1, XTk.postWindowCreationKey, TIPostWindowCreation, ti]; XTk.RegisterNotifier[wid1, XTk.bindScreenLRKey, TIBindScreen, ti]; XTk.RegisterNotifier[wid1, XTk.forgetScreenLRKey, TIForgetScreen, ti]; XTkWidgets.BindScreenShell[shell, ui.c]; ti.help ¬ XTkHelpShells.CreateHelpWithPopShell[shell]; XTkWidgets.RealizeShell[shell]; }; KeyHit: XTkWidgets.ButtonHitProcType = { ui: UIInstance ~ NARROW[registerData]; text: Rope.ROPE ¬ NARROW[callData]; WITH event SELECT FROM br: Xl.ButtonReleaseEvent => IF br.button>1 THEN text ¬ Rope.Translate[base: text, translator: Rope.Upper]; ENDCASE => {}; MakeFinal[ui]; InsertRope[ui, text]; Push[ui]; }; KeyboardPainter: TYPE = PROC [L: PROC [char: CHAR], Lx: PROC [key, text: Rope.ROPE, extraWidth: INT], Home: PROC, Down: PROC, Right: PROC [amount: INT], sz: Xl.Size]; CreateKeyboard: PROC [ui: UIInstance, basicWidth: INT ¬ 19, basicHeight: INT ¬ 19, rows, cols: INT, painter: KeyboardPainter] ~ { next: Xl.Point ¬ [0, 0]; sz: Xl.Size ¬ [basicWidth, basicHeight]; Home: PROC [] = { next.x ¬ 0; next.y ¬ 0; }; Down: PROC [] = { next.y ¬ next.y + sz.height; next.x ¬ 0; }; Right: PROC [amount: INT] = { next.x ¬ next.x + amount; }; L: PROC [char: CHAR] = { key: Rope.ROPE ¬ Rope.FromChar[Ascii.Lower[char]]; text: Rope.ROPE ¬ Rope.FromChar[char]; Lx[key, text]; }; Lx: PROC [key, text: Rope.ROPE, extraWidth: INT ¬ 0] = { b: XTkWidgets.TextWidget ¬ XTkWidgets.CreateButton[ widgetSpec: [geometry: [pos: next, size: [width: sz.width-2+extraWidth, height: sz.height-2], borderWidth: 1]], text: text, hitProc: KeyHit, registerData: ui, callData: key ]; Right[sz.width+extraWidth]; XTkWidgets.AppendChild[keyboard, b]; }; shell: XTkWidgets.Widget ¬ XTkWidgets.CreateShell[windowHeader: "pseudo-keyboard", className: $PseudoKeyboard]; keyboard: XTkWidgets.Widget ¬ XTkWidgets.CreateContainer[ widgetSpec: [ geometry: [size: [width: cols*basicWidth, height: rows*basicHeight]] ] ]; XTkWidgets.SetShellChild[shell, keyboard]; painter[L, Lx, Home, Down, Right, sz]; XTkWidgets.BindScreenShell[shell, ui.c]; XTkOps.SetupDestruction[shell, ui.label, XTk.forgetScreenKey]; XTkWidgets.RealizeShell[shell]; }; CreateAlphaKeyboard: XTkWidgets.ButtonHitProcType ~ { PaintAlpha: KeyboardPainter = { Home[]; L['Q]; L['W]; L['E]; L['R]; L['T]; L['Y]; L['U]; L['I]; L['O]; L['P]; Down[]; Right[sz.width/3]; L['A]; L['S]; L['D]; L['F]; L['G]; L['H]; L['J]; L['K]; L['L]; Down[]; Right[sz.width/3*2]; L['Z]; L['X]; L['C]; L['V]; L['B]; L['N]; L['M]; Lx[" ", "sp", 6]; }; CreateKeyboard[NARROW[registerData], 19, 19, 3, 10, PaintAlpha]; }; CreateSpecialKeyboard: XTkWidgets.ButtonHitProcType ~ { PaintSpecial: KeyboardPainter = { Home[]; L['1]; L['2]; L['3]; L['4]; L['5]; L['6]; L['7]; L['8]; L['9]; L['0]; Down[]; L['!]; L['@]; L['#]; L['$]; L['%]; L['~]; L['&]; L['*]; L['(]; L[')]; ForkOps.ForkDelayed[100, ReadDefaultTraining]; Down[]; L['-]; L['+]; L['=]; L['\\]; L['|]; L['[]; L[']]; L['{]; L['}]; L['?]; Down[]; L[':]; L[';]; L['"]; L['']; L[',]; L['.]; L['/]; L['<]; L['>]; L[' ]; }; CreateKeyboard[NARROW[registerData], 19, 19, 4, 10, PaintSpecial]; }; TrainingSelection: XTkPopUps.CreateWidgetProc ¬ { choiceList: XTkPopUps.ChoiceList ¬ NIL; EachEntry: SymTab.EachPairAction = { help: Rope.ROPE ~ "read a personalized training database"; choiceList ¬ CONS[[key, val, NIL, help, DBSelectorHit], choiceList]; }; [] ¬ SymTab.Pairs[trainingMenuTab, EachEntry]; choiceList ¬ CONS[["uni-strokes", $u, NIL, "recognize uni strokes", DBSelectorHit], choiceList]; choiceList ¬ CONS[["default database", $m, NIL, "default latin characters", DBSelectorHit], choiceList]; RETURN [XTkPopUps.CreateSimplePopUpButton[text: "select training", list: choiceList, defaultNotify: DBSelectorHit, registerData: registerData, help: "select a user dependent training database"]]; }; trainingMenuTab: SymTab.Ref ~ SymTab.Create[]; TrainingMenuCommand: Commander.CommandProc ~ { list: LIST OF Rope.ROPE ¬ NIL; key: Rope.ROPE ¬ CommanderOps.NextArgument[cmd]; IF Rope.IsEmpty[key] THEN ERROR CommanderOps.Failed["needs a menu entry and a list of file names"]; FOR file: Rope.ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd] WHILE file#NIL DO IF ~Rope.IsEmpty[file] THEN list ¬ CONS[file, list]; ENDLOOP; IF list=NIL THEN [] ¬ SymTab.Delete[trainingMenuTab, key] ELSE [] ¬ SymTab.Store[trainingMenuTab, key, list] }; ForkOps.ForkDelayed[50, ReadDefaultTraining]; Commander.Register["X11Handwriting", HWRCommand, "Open a handwriting recognition widget"]; Commander.Register["X11Handwriting-TrainingMenu", TrainingMenuCommand, "Update the menu for training databases"]; END. „X11HandwritingImpl.mesa Copyright Ó 1992, 1993 by Xerox Corporation. All rights reserved. Christian Jacobi, October 14, 1992 11:15 am PDT Christian Jacobi, May 11, 1993 12:30 pm PDT Handwriting recognition user interface Make up a new file name base We do want to make up the name instead of allowing user input so a handwriting recognition widget can safely handed out to an untrusted user --Restricts rope to single line of finite small length --Useful to clean externally aquired ropes before stuffing them into the displayer --display the normalized strokes regressionsName: Rope.ROPE ¬ XTkWidgets.GetText[ti.regressionsName]; name: Rope.ROPE ¬ XTkWidgets.GetText[ti.trainingName]; Assumes caller catches Xl.XError Good fonts 12x24 -xerox-helvetica-medium-r-normal--24-240-75-75-p-170-iso8859-1 -xerox-helvetica-medium-r-normal--36-360-75-75-p-250-iso8859-1 -- -- -- -- -- -- -- -- -- Ê!Ò•NewlineDelimiter –(cedarcode) style˜code™Kšœ Ïeœ7™BK™/K™+K™K™&K™—šÏk œ1žœžœžœÛ˜¸K˜—šÏnœžœž˜!Kšžœœ˜£—šž˜K˜—K˜+K˜(K˜!K˜Kšœfžœ]˜ÆšŸ œžœ#žœ˜KKšžœ+˜1K˜—K˜Kšœžœžœžœ ˜!Kšœ žœžœžœ ˜ K˜Kšœ žœžœ˜%šœžœžœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜K˜%Kšœžœ˜Kšœžœ˜Kšœ žœžœ žœ˜Kšœžœžœ˜ Kšœžœžœ˜Kšœžœ˜Kšœ žœžœ˜Kšœ žœ˜Kšœ žœ˜Kšœ žœžœ˜Kšœžœ˜!Kšœ'žœ˜+Kšœ žœžœžœ˜*Kšœ žœžœž˜+Kšœž˜!K˜K˜—Kšœžœžœ˜1šœžœžœ˜$Kšœžœ˜Kšœžœ˜Kšœžœ˜ Kšœž˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜"Kšœžœ˜Kšœžœ˜Kšœ+ž˜.K˜—K˜Kšœ žœ˜š Ÿ œžœžœ žœžœ žœ˜FK™K™ŒKšœžœ#žœžœ ˜LK˜K˜—K˜š Ÿ œžœ žœžœžœ˜8J™7J™RKšœžœ˜Kšœžœ˜ Kšœžœ˜Kšžœ žœ,˜>K˜!Kšžœ žœ˜.Kšžœ˜ K˜—K˜š Ÿœžœžœ(žœžœ˜qKšžœžœžœ*˜8K˜K˜—š Ÿœžœžœžœžœ˜(Jšžœžœžœžœ˜"Jšžœžœ˜J˜J˜—šŸ œ˜#Kšœžœ˜&Kšœ žœ˜Kšœžœ˜Kšœžœ˜1K˜OKšœžœ<˜BKšœžœ.˜4K˜>K˜>K˜K˜—šŸœ˜"Kšœžœ˜&K˜K˜K˜—šŸœ˜!Kšœžœ˜&šžœ ž˜˜ K˜"K˜—˜K˜3K˜—K˜7K˜!K˜#K˜ Kšžœ˜ —K˜K˜—šŸœ˜Kšœžœ˜&šžœ ž˜˜ Jšœ žœ˜Kšœžœ˜#K˜2K˜—˜ Jšœ žœ"˜1Kšœžœ˜#K˜2K˜—˜ K˜2K˜—˜ K˜2K˜—Kšžœ˜ —K˜K˜—šŸœ˜šŸœžœžœ˜*K˜4K˜-Kšœ]žœ˜xK˜—Kšœžœ˜&šžœ ž˜˜ Kšœ žœžœ˜ Kšœ$žœ˜*K˜ K˜—˜ Kšœ žœžœ˜ Kšœ7žœ˜=K˜ K˜—˜ Kšœ žœžœ˜K˜—˜ Kšœ žœžœ˜K˜—˜ Kšœžœ˜#K˜ K˜—˜ K˜ K˜—˜ Kšœžœ˜'K˜K˜"K˜K˜"K˜ K˜—Kšžœ˜ —K˜K˜—šŸœžœ(žœžœ˜lšžœžœžœ˜Jšœžœ˜"Jšœžœ˜#Kšœ/žœ ˜K™>—K˜šŸœ˜(Kšœžœ˜&Kšœ0žœ˜5Kšžœžœžœ˜K˜K˜—šŸ œ˜&Kšœžœ˜,K˜K˜)K˜,K˜+šžœžœžœ˜K˜5Kšžœžœ˜K˜—K˜K˜—šŸœ˜.Kšœžœ˜,K˜šžœžœžœ˜J˜IJ˜—K˜K˜—šŸœ˜1Kšœžœ ˜*K˜šžœžœžœžœžœžœžœžœ˜³K˜šžœ žœžœ˜Kšœ>žœ˜HK˜—K˜—K˜K˜—šŸœ˜(šŸœžœžœ/˜NKš žœžœžœžœžœ˜=K˜—Kšœžœ˜,K˜Kšžœžœžœ˜+K˜K˜—šŸ œžœ˜$Kšœžœ˜"Kšœžœ˜#Kšœ žœžœ ˜.Kšœ žœ ˜šžœ ž˜K˜RJ˜Jšžœ˜ —K˜K˜—šŸ œžœ˜$K˜8K˜4Jšžœžœ˜%K˜K˜—šŸ œžœ=žœžœ)˜}šžœžœžœ˜šž˜Jšœžœžœ˜%š žœžœžœžœžœ˜K˜K˜K˜SJ˜J˜—Jšž˜—K˜—K˜K˜—šŸ œžœžœ˜-Kš œžœžœžœžœ˜(Kš œžœžœžœžœ˜(K˜šžœžœžœž˜-Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšžœ˜—K˜—K˜šŸ œžœe˜všžœžœžœž˜/K˜&Kšžœ˜—K˜K˜—šŸœžœ˜Kšœ žœ˜K˜#K˜+K˜K˜—š Ÿ œžœžœ žœžœ˜FKšœžœ˜Kšœžœ˜K˜Kšœžœ˜K˜ K˜K˜—šŸ œžœžœ˜5Kšœ žœ˜Kšœžœžœ&˜3K˜MK˜'K˜K˜—šŸœžœžœ˜.Kšœ žœ˜Kšœžœ˜ Kšœžœžœ˜(šžœžœ˜K˜^K˜—K˜K˜—šŸœžœžœžœ ˜;Kšœ žœ˜Kšœžœžœ&˜3šžœžœ˜K˜K˜—K˜—K˜š Ÿ œžœžœ žœžœ˜HKšœ žœ˜Kšœžœžœ&˜3šžœ˜šžœ˜K˜MK˜K˜ K˜—Kšžœžœ˜"—K˜K˜—šŸ œžœžœ˜0šžœ žœžœ˜K˜Kšžœ žœžœ˜0šžœ žœžœ˜Kšœžœ˜)K˜K˜—K˜—Kšœžœ˜K˜ Kšœ˜K˜—šŸ œžœžœ˜.Kšœžœ˜šžœž˜ ˜ šžœž˜Kšžœ&˜(Kšžœ&˜(K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜KšžœZ˜\Kšžœ˜—K˜—K˜K˜˜ šžœž˜Jšžœ/˜1JšžœB˜DKšžœZ˜\Jšžœ˜—K˜—Kšžœ˜—K˜Kšœžœ˜K˜ K˜K˜—šŸœžœžœ˜+Kšœ žœ˜Kšœžœ˜šžœžœ˜Kšžœ˜Kšžœ$˜(—Kšœžœ˜Kšœžœ˜K˜ Kšœ˜K˜—šŸœžœžœ˜3Kšœžœg˜mK˜K˜ K˜K˜—šŸœžœžœ˜8Kšœžœh˜nK˜K˜K˜—š Ÿœžœžœ žœžœ˜JKšœ žœ˜Kšœžœ˜ K˜vK˜vKšœ žœžœ˜)Kšœ žœžœ˜(šžœžœ˜Kšœ žœ˜%Kšœ žœ˜"K˜—K˜VK˜K˜ šžœ ž˜K˜B—K˜—K˜šŸ œ'˜3Kšœžœ˜K˜K˜"šžœžœ˜%šžœ˜K˜&K˜—šžœ˜Kšœžœžœ6žœ ˜Sšžœžœžœ˜šžœž˜Kšžœ.˜1Kšžœ!žœ˜+šžœ ˜ K˜+K˜K˜—šžœ ˜ Kšœžœ˜'K˜K˜K˜ K˜—šžœ ˜ Kšœžœ˜"K˜&K˜K˜ K˜—Kšžœ!žœ˜*šžœ ˜ K˜+K˜K˜—Kšžœ˜—K˜—K˜——K˜K˜—š Ÿœžœžœžœ žœžœ ˜>Kšžœžœžœžœ˜šžœžœžœž˜)Kšžœžœžœžœ˜'Kšž˜—K˜—K˜šŸ œžœ'˜8K˜K˜K˜K˜K˜—K˜šŸ œ'˜2šžœžœžœ˜Kšœžœ˜K˜Kšœžœžœ˜0šžœžœ˜$šžœ˜K˜K˜Kšœžœ ˜$K˜K˜—šžœ˜Kšœ žœ˜ K˜——K˜K˜K˜—K˜K˜—šŸ œ˜%Kšœžœ˜$K™˜Kšœžœ˜ Kšœžœ!˜4Kšœžœžœ;˜WKšœžœžœ?˜\Kš œžœžœžœ>žœ˜`Kšœžœ'˜EK˜—Kšœžœ˜K˜K˜—K˜tK˜|K˜uK˜rK˜uK˜uKšœ?žœE˜ˆK˜qK˜tK˜yK˜rK˜wK˜tKšœ@žœ5˜yK™K˜?˜-K˜:K˜—K™Kšœ>žœ'˜iKšœžœ,˜5K˜yKšœ0žœ˜9Kšœ!žœ˜'K˜3Kšœ#žœ˜)K˜AK˜EK˜:Kšœžœ˜–KšœZžœžœ˜tKšœ"žœ˜(K˜K˜"K˜K˜—šŸœžœžœžœ˜4Kšœžœ˜šžœžœžœ˜K˜Kšžœžœžœ=˜NK˜—K˜—K˜šŸœ˜!Kšœžœ$˜9Kšœ…žœžœ˜K˜—K˜šŸ œ˜'Kšœ žœžœ˜Kšœžœ˜&K˜(šžœ žœž˜šœžœ˜ šžœ ž˜K˜K˜Kšžœ˜—K˜—šœžœžœžœ˜Jšœžœ˜!š žœžœžœžœžœžœž˜=šžœ#žœ˜,Jšžœ/˜3šžœ˜Jšœžœ˜ šœ$žœžœ ˜K˜tK˜vK˜t˜RK˜K˜K˜—KšœBžœC˜‰K™˜@K˜:K˜—˜@K˜:K˜—˜@K˜:K˜—K˜DK˜MK˜MKšœ7žœ ˜HKšœ7žœ ˜HKšœ7žœ ˜HKšœ7žœ˜IKšœ>žœ1˜sK˜K˜PK˜BK˜FK˜(K˜6K˜K˜K˜—šŸœ"˜(Kšœžœ˜&Kšœ žœžœ ˜#šžœžœž˜˜Kšžœ žœ;˜N—Kšžœ˜—K˜K˜K˜ K˜K˜—Kš!œžœžœŸœžœžœŸœžœžœžœŸœžœŸœžœŸœžœ žœ˜¦K˜š Ÿœžœžœžœžœ˜K˜K˜(šŸœžœ˜K˜Kšœ˜—šŸœžœ˜K˜K˜ Kšœ˜—šŸœžœ žœ˜K˜K˜—šŸœžœžœ˜Kšœ žœ$˜2Kšœ žœ˜&J˜K˜—šŸœžœžœžœ ˜8˜3K˜pK˜Kšœ˜Kšœ˜K˜—šŸœ"˜5šŸ œ˜K˜KšœE˜EK˜Kšœ?˜?K˜K˜BK˜—Kšœžœ+˜@Kšœ˜K˜—šŸœ"˜7šŸ œ˜!K˜K˜EK˜K˜EItrainingregcommand˜.K˜KšœF˜FK˜K˜EK˜—Kšœžœ-˜BKšœ˜K˜—šŸœ ˜1Kšœ#žœ˜'šŸ œ˜$Kšœ žœ+˜:Kšœ žœ žœ$˜DK˜—K˜.Kšœ žœžœ7˜`Kšœ žœžœ:˜hKšžœ½˜ÃL˜—L˜L˜.L˜šŸœ˜.Lš œžœžœžœžœ˜Lšœ žœ"˜0šžœžœ˜KšžœD˜I—š žœ žœBžœžœž˜fLšžœžœžœ ˜4Lšžœ˜—šžœžœ˜ Lšžœ)˜-Lšžœ.˜2—L˜—L˜L˜-K˜ZK˜qKšžœ˜K˜—…—‘–¶ì