<<>> <> <> <> <> DIRECTORY Ascii, ColumnLs, Commander, CommanderOps, Convert, CtBasic, CtDispatch, CtFile, CtViewer, Imager, IO, MessageWindow, PFS, PreDebug, Real, Rope, SF, ViewerClasses, ViewerOps; CtDispatchImpl: CEDAR PROGRAM IMPORTS ColumnLs, Commander, CommanderOps, Convert, CtBasic, CtFile, CtViewer, IO, MessageWindow, PFS, PreDebug, Real, Rope, SF, ViewerOps EXPORTS CtDispatch ~ BEGIN <> CommandProc: TYPE ~ Commander.CommandProc; CmdHandle: TYPE ~ Commander.Handle; Args: TYPE ~ CommanderOps.ArgumentVector; SampleMaps: TYPE ~ CtBasic.SampleMaps; CtProc: TYPE ~ CtDispatch.CtProc; OpType: TYPE ~ CtDispatch.OpType; Op: TYPE ~ CtDispatch.Op; Ops: TYPE ~ CtDispatch.Ops; OpsSequence: TYPE ~ CtDispatch.OpsSequence; ViewerProc: TYPE ~ CtViewer.ViewerProc; Context: TYPE ~ Imager.Context; ROPE: TYPE ~ Rope.ROPE; Box: TYPE ~ SF.Box; Viewer: TYPE ~ ViewerClasses.Viewer; <> ctOps, cmOps: Ops ¬ NIL; delimiter: ROPE ¬ ","; AddToOps: PROC [ ops: Ops, name: ROPE, type: OpType, proc: CtProc, usage: ROPE, needViewer, makeViewer: BOOL ¬ TRUE] RETURNS [Ops] ~ { op: Op ¬ [name, type, proc, usage, needViewer, makeViewer]; IF proc # NIL THEN op.usage ¬ Rope.Concat[" ", usage]; IF ops # NIL THEN FOR n: INT IN [0..ops.length) DO IF NOT Eq[name, ops[n].name] THEN LOOP; ops[n] ¬ op; RETURN[ops]; ENDLOOP; IF ops = NIL THEN ops ¬ NEW[OpsSequence[10]]; IF ops.length = ops.maxLength THEN { old: Ops ~ ops; ops ¬ NEW[OpsSequence[Real.Round[1.3*old.length]]]; FOR i: INT IN [0..old.length) DO ops[i] ¬ old[i]; ENDLOOP; ops.length ¬ old.length; }; ops[ops.length] ¬ op; ops.length ¬ ops.length+1; RETURN[ops]; }; RegisterCmOp: PUBLIC PROC [name: ROPE, proc: CtProc, usage: ROPE] ~ { cmOps ¬ AddToOps[cmOps, name, cm, proc, usage]; }; RegisterCtOp: PUBLIC PROC [ name: ROPE, proc: CtProc, usage: ROPE, needViewer: BOOL ¬ TRUE, makeViewer: BOOL ¬ TRUE] ~ { ctOps ¬ AddToOps[ctOps, name, ct, proc, usage, needViewer, makeViewer]; }; FindOp: PROC [name: ROPE, ops: Ops] RETURNS [op: Op] ~ { IF ops # NIL THEN FOR n: INT IN [0..ops.length) DO IF Eq[name, ops[n].name] THEN RETURN[ops[n]]; ENDLOOP; }; GetCmOp: PUBLIC PROC [name: ROPE] RETURNS [Op] ~ {RETURN[FindOp[name, cmOps]]}; GetCtOp: PUBLIC PROC [name: ROPE] RETURNS [Op] ~ {RETURN[FindOp[name, ctOps]]}; UnregisterAll: PUBLIC PROC ~ {ctOps ¬ cmOps ¬ NIL}; <> DispatchData: TYPE ~ RECORD [cmd: CmdHandle, ops: Ops, box: Box, args: Args, wDir: ROPE]; PrintOps: PROC [ops: Ops, cmd: CmdHandle] ~ { ColumnWidth: PROC RETURNS [INT] ~ { names: LIST OF ROPE ¬ NIL; FOR n: INT IN [0..ops.length) DO IF ops[n].proc # NIL THEN names ¬ CONS[ops[n].name, names]; ENDLOOP; RETURN[ColumnLs.ColumnWidth[cmd, names]]; }; names: LIST OF ROPE ¬ NIL; colWidth: INT ¬ ColumnWidth[]; IF ops = NIL THEN {IO.PutRope[cmd.out, "no registered commands\n"]; RETURN}; IO.PutF[cmd.out, "\t\t\t\t\t\t%l%g Commands%l\n", IO.rope["bz"], IO.rope[IF ops = ctOps THEN "Ct" ELSE "Cm"], IO.rope["BZ"]]; FOR n: INT IN [0..ops.length) DO IF ops[n].proc = NIL THEN { ColumnLs.ColumnateGivenColumnWidth[cmd, names, colWidth, TRUE, fixed,,, "\t"]; IO.PutF[cmd.out, "%l%g%l\n", IO.rope["b"], IO.rope[ops[n].name], IO.rope["B"]]; names ¬ NIL; } ELSE names ¬ CONS[ops[n].name, names]; ENDLOOP; ColumnLs.ColumnateGivenColumnWidth[cmd, names, colWidth, TRUE, fixed,,, "\t"]; }; Eq: PROC [r1, r2: ROPE] RETURNS [BOOL] ~ {RETURN[Rope.Equal[r1, r2, FALSE]]}; Union: PROC [a, b: Box] RETURNS [u: Box] ~{u ¬ [SF.Min[a.min, b.min], SF.Max[a.max, b.max]]}; GetOp: PROC [ops: Ops, name: ROPE] RETURNS [op: Op ¬ [NIL, ct, NIL, NIL, TRUE, TRUE]] ~ { IF ops # NIL THEN FOR i: INT IN [0..ops.length) DO IF Eq[name, ops[i].name] THEN {op ¬ ops[i]; EXIT}; ENDLOOP; }; GetOps: PROC [cmd: Commander.Handle] RETURNS [ops: Ops] ~ { CmdIs: PROC [r: ROPE] RETURNS [b: BOOL] ~ {b ¬ Rope.Find[cmd.command, r,, FALSE] # -1}; ops ¬ IF CmdIs["Cm"] OR CmdIs["ColorTrixMap"] THEN cmOps ELSE ctOps; }; ActionError: ERROR [reason: ROPE] = CODE; Action: ViewerProc ~ { Abort: PROC [reason: ROPE] ~ { affectedRegion ¬ [[0, 0], [0, 0]]; ActionError[reason]; }; DoOp: PROC [arg: INTEGER] ~ { region: Box; args, error: ROPE ¬ NIL; op: Op ¬ GetOp[d.ops, d.cmd.command ¬ d.args[arg]]; IF op.needViewer AND NOT SF.Nonempty[d.box] THEN Abort["empty box"]; FOR i: INT IN [arg+1..d.args.argc) DO IF Eq[d.args[i], delimiter] THEN EXIT ELSE args ¬ Rope.Cat[args, " ", d.args[i]]; ENDLOOP; d.cmd.commandLine ¬ args; [error, region] ¬ op.proc[viewer, maps, context, d.cmd, PFS.RopeFromPath[PFS.GetWDir[]]]; IF error # NIL THEN Abort[error]; affectedRegion ¬ Union[affectedRegion, region]; }; d: REF DispatchData _ NARROW[clientData]; CtBasic.ReIndexMaps[maps,, d.box]; affectedRegion ¬ [[0, 0], [0, 0]]; DoOp[1]; FOR a: INT IN [2..d.args.argc-1) DO IF Eq[d.args[a], delimiter] THEN DoOp[a+1]; ENDLOOP; <> <> <> }; Dispatch: PUBLIC CommandProc ~ { Check: PROC RETURNS [bad: BOOL ¬ FALSE, needViewer, makeViewer: BOOL ¬ FALSE] ~ { Test: PROC [name: ROPE] ~ { op: Op ¬ GetOp[d.ops, name]; IF op.proc = NIL THEN{ bad ¬ TRUE; IO.PutF1[cmd.out, "no such option: %g\n", IO.rope[name]]; }; IF op.type = ct AND GetOp[d.ops, name].needViewer THEN needViewer ¬ TRUE; IF op.type = ct AND GetOp[d.ops, name].makeViewer THEN makeViewer ¬ TRUE; }; Test[args[1]]; FOR i: INT IN [2..args.argc-1) DO IF Eq[args[i], delimiter] THEN Test[args[i+1]]; ENDLOOP; }; Reject: PROC [reason: ROPE] RETURNS [reject: BOOL ¬ FALSE] ~ { IO.PutRope[cmd.out, Rope.Concat[reason, "\n"]]; result ¬ $Failure; }; DoOps: PROC ~ { ENABLE { UNCAUGHT => { IF debug THEN ERROR; IO.PutRope[cmd.out, "unknown error"]; GOTO Bad; }; ActionError => { IO.PutF1[cmd.out, "error: %g\n", IO.rope[reason]]; GOTO Bad; }; }; IF v # NIL THEN [] ¬ CtViewer.DoWithViewer[v, Action, d] ELSE [] ¬ Action[NIL, maps, NIL, d]; EXITS Bad => NULL; }; v: Viewer ¬ NIL; in, out: ROPE ¬ NIL; maps: SampleMaps ¬ NIL; args: Args ¬ CommanderOps.Parse[cmd]; d: REF DispatchData ¬ NEW[DispatchData ¬ [cmd, GetOps[cmd],,, PFS.RopeFromPath[PFS.GetWDir[]]]]; IF args.argc > 3 THEN FOR i: INT IN [1..args.argc-2) DO IF Eq[args[i], "-file"] THEN {in ¬ args[i+1]; out ¬ args[i+2]; EXIT}; ENDLOOP; SELECT TRUE FROM args.argc < 2 OR Rope.IsEmpty[args[1]] => RETURN[$Failure, "no operation specified"]; Eq[args[1], "?"] => {PrintOps[d.ops, cmd]; RETURN}; Check[].bad => RETURN[$Failure]; args.argc > 2 AND Eq[args[2], "?"] => RETURN[msg: Rope.Concat["Usage:", GetOp[d.ops, args[1]].usage]]; in # NIL AND out # NIL => maps ¬ CtFile.ReadFile[in]; Check[].needViewer => { ViewerName: PROC RETURNS [name: ROPE] ~ { FOR i: INT IN [1..args.argc-1) DO IF Eq[args[i], "-name"] THEN RETURN[args[i+1]]; ENDLOOP; name ¬ IF CtViewer.currentViewer # NIL AND NOT CtViewer.currentViewer.destroyed THEN CtViewer.currentViewer.name ELSE "ColorTrix"; }; GetColumnInfo: PROC RETURNS [ColumnInfo] ~ { FOR i: INT IN [1..args.argc) DO IF Eq[args[i], "-right"] THEN RETURN[[TRUE, right]]; IF Eq[args[i], "-left"] THEN RETURN[[TRUE, left]]; ENDLOOP; RETURN[[FALSE, left]]; }; ColumnInfo: TYPE ~ RECORD [useColumn: BOOL, column: ViewerClasses.Column]; columnInfo: ColumnInfo ¬ GetColumnInfo[]; name: ROPE ¬ ViewerName[]; IF (v ¬ ViewerOps.FindViewer[name]) = NIL AND Rope.Find[name, "/"] = -1 THEN v ¬ ViewerOps.FindViewer[Rope.Concat[d.wDir, name]]; IF v = NIL AND Check[].makeViewer THEN v ¬ CtViewer.GetViewer[name, columnInfo.column]; IF columnInfo.useColumn AND columnInfo.column # v.column THEN ViewerOps.ChangeColumn[v, columnInfo.column]; IF v.iconic THEN ViewerOps.OpenIcon[v]; }; ENDCASE; d.box ¬ GetBox[v, cmd]; RemoveArguments[cmd, "-w", 4]; RemoveArguments[cmd, "-wi", 0]; RemoveArguments[cmd, "-wp", 0]; RemoveArguments[cmd, "-file", 2]; RemoveArguments[cmd, "-name", 1]; RemoveArguments[cmd, "-left", 0]; RemoveArguments[cmd, "-right", 0]; d.args ¬ CommanderOps.Parse[cmd]; -- now with "extraneous" arguments removed <> [] ¬ PreDebug.Protect[DoOps, Reject]; IF in # NIL AND out # NIL THEN CtFile.WriteMapsToAIS[maps, out, cmd.out]; IF v # NIL THEN CtViewer.currentViewer ¬ v; }; <> lastBox: Box ¬ [[0, 0], [0, 0]]; GetBox: PUBLIC PROC [viewer: Viewer, cmd: CmdHandle, clip: Box ¬ SF.maxBox] RETURNS [b: Box] ~ { setLast: BOOL ¬ FALSE; v: Viewer ¬ viewer; args: Args ¬ CommanderOps.Parse[cmd]; bnds: Box ¬ b ¬ IF v # NIL THEN [[v.cy-1, v.cx-1], [v.cy+v.ch-1, v.cx+v.cw-1]] ELSE SF.maxBox; FOR n: INT IN [1..args.argc) DO ENABLE Convert.Error => GOTO Bad; SELECT TRUE FROM Eq[args[n], "-wp"] => RETURN[lastBox]; Eq[args[n], "-wi"] AND v # NIL => IF v.class.flavor = $CtViewer THEN { c: CHAR; CtViewer.EnableBoundingBox[v]; IO.PutRope[cmd.out, "Select region: to continue, to abort: "]; c ¬ IO.GetChar[cmd.in ! IO.Error => CONTINUE; IO.Rubout => {IO.PutRope[cmd.out, "\n"]; CONTINUE}]; b ¬ CtViewer.DisableBoundingBox[v]; IF c # Ascii.LF THEN b ¬ [[0, 0], [0, 0]]; setLast ¬ TRUE; } ELSE { IO.PutRope[cmd.out, "no interactive selection for this viewer\n"]; b ¬ [[0, 0], [0, 0]]; }; Eq[args[n], "-w"] => { IF n >= args.argc-4 THEN GOTO Bad ELSE { x: INT ¬ Convert.IntFromRope[args[n+1]]; y: INT ¬ Convert.IntFromRope[args[n+2]]; w: INT ¬ Convert.IntFromRope[args[n+3]]; h: INT ¬ Convert.IntFromRope[args[n+4]]; b ¬ [[y, x], [y+h, x+w]]; setLast ¬ TRUE; }; EXIT; }; ENDCASE; ENDLOOP; b ¬ SF.Intersect[bnds, SF.Intersect[SF.Intersect[clip, b], [SF.zeroVec, SF.maxVec]]]; IF SF.Empty[b] THEN b ¬ [[0, 0], [0, 0]]; IF setLast THEN lastBox ¬ b; EXITS Bad => b ¬ [[0, 0], [0, 0]]; }; PrintBox: PROC [name: ROPE, b: Box] ~ { MessageWindow.Append[ IO.PutFLR["%g: [%g,%g], [%g,%g] ", LIST[IO.rope[name], IO.int[b.min.s], IO.int[b.min.f], IO.int[b.max.s], IO.int[b.max.f]]], TRUE]; }; RemoveArguments: PROC [cmd: Commander.Handle, key: ROPE, nArgs: INT ¬ 0] ~ { i: INT ¬ 1; args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd, leaveQuotes]; cmd.commandLine ¬ NIL; WHILE i < args.argc DO IF Rope.Equal[args[i], key, FALSE] THEN i ¬ i+nArgs ELSE cmd.commandLine ¬ Rope.Cat[cmd.commandLine, " ", args[i]]; i ¬ i+1; ENDLOOP; }; <> CtUndo: CtProc ~ { buffer: SampleMaps ¬ CtViewer.GetBuffer[viewer]; action: SampleMaps ¬ CtViewer.GetSave[viewer]; affect ¬ [[0, 0], [0, 0]]; -- disable subsequent buffering (it's done here) IF buffer # NIL AND action # NIL THEN { CtBasic.CopyMaps[action, buffer]; CtBasic.CopyMaps[action, maps]; }; }; <> debug: BOOL ¬ FALSE; CtDebug: CtProc ~ { args: Args ¬ CommanderOps.Parse[cmd]; IF args.argc = 2 THEN SELECT TRUE FROM Eq[args[1], "on"] => CtViewer.Debug[debug ¬ TRUE]; Eq[args[1], "off"] => CtViewer.Debug[debug ¬ FALSE]; ENDCASE; }; <> usage: ROPE ~ " Ct (or ColorTrix) [arguments] [-option] Cm (or ColorTrixMap) [arguments] Ct options: -name use (or create) the named viewer -left move or open the viewer in the left column -right move or open the viewer in the right column -file read from in AIS (triplet) and write to out -w apply function to the given window -wi apply function to interactively given window -wp apply function using previous window 'Ct ?' or 'Cm ?' will list the set of corresponding functions. 'Ct/Cm ?' prints a usage message for the function."; Commander.Register["Ct", Dispatch, usage]; Commander.Register["ColorTrix", Dispatch, usage]; Commander.Register["Cm", Dispatch, usage]; Commander.Register["ColorTrixMap", Dispatch, usage]; RegisterCtOp["Undo, Debugging:", NIL, NIL]; RegisterCtOp["Undo", CtUndo, "undo the previous viewer action", TRUE, FALSE]; RegisterCtOp["Debug", CtDebug, " if on, don't catch errors", FALSE, FALSE]; <<>> END.