CtDispatchImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, December 7, 1992 3:40 pm PST
Andrew Glassner November 14, 1990 12:50 pm PST
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
Types and Globals
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;
Color Trix Registry
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};
Dispatcher
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;
IF d.ops = cmOps THEN CtViewer.PutColormap[
IF viewer # NIL THEN viewer ELSE CtViewer.currentViewer,
CtMap.Read[]];
};
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
IF v # NIL THEN CtMap.Write[CtViewer.GetColormap[v]];
[] ¬ PreDebug.Protect[DoOps, Reject];
IF in # NIL AND out # NIL THEN CtFile.WriteMapsToAIS[maps, out, cmd.out];
IF v # NIL THEN CtViewer.currentViewer ¬ v;
};
Support
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: <CR> to continue, <DEL> 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;
};
Undo
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
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;
};
Start Code
usage: ROPE ~ "
Ct (or ColorTrix) <image-function> [arguments] [-option]
Cm (or ColorTrixMap) <colormap-function> [arguments]
Ct options:
 -name <viewer> 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 <in> <out> read from in AIS (triplet) and write to out
 -w <x y w h> 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 <function> ?' 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, "<on | off> if on, don't catch errors", FALSE, FALSE];
END.