CtPicturesCommandImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, December 10, 1992 11:39 pm PST
Heckbert, June 23, 1988 8:42:17 pm PDT
DIRECTORY Args, Buttons, CedarProcess, Commander, CommanderOps, Containers, Controls, CtBasic, CtDispatch, CtFile, CtMap, FileNames, FS, Imager, IO, MessageWindow, Process, Real, Rope, SF, ViewerClasses, ViewerOps;
CtPicturesCommandImpl: CEDAR PROGRAM
IMPORTS Args, Buttons, CedarProcess, CommanderOps, Containers, Controls, CtBasic, CtDispatch, CtFile, CtMap, FileNames, FS, IO, MessageWindow, Process, Real, Rope, ViewerOps
~ BEGIN
Types
ButtonProc:  TYPE ~ Buttons.ButtonProc;
SampleMap:  TYPE ~ CtBasic.SampleMap;
SampleMaps:  TYPE ~ CtBasic.SampleMaps;
RGB:    TYPE ~ CtBasic.RGB;
CtProc:   TYPE ~ CtDispatch.CtProc;
Cmap:    TYPE ~ CtMap.Cmap;
Color:    TYPE ~ Imager.Color;
ConstantColor: TYPE ~ Imager.ConstantColor;
STREAM:   TYPE ~ IO.STREAM;
ROPE:    TYPE ~ Rope.ROPE;
Viewer:   TYPE ~ ViewerClasses.Viewer;
ViewerClassRec: TYPE ~ ViewerClasses.ViewerClassRec;
Gallery Command
Gallery:    TYPE ~ RECORD [aisName, cmap: ROPE ¬ NIL];
GalleryData:   TYPE ~ RECORD [
cmd:       Commander.Handle ¬ NIL,
ctView:      CtProc ¬ NIL,
image:      Viewer ¬ NIL,
wDir:       ROPE ¬ NIL,
parent:      Viewer ¬ NIL,
maps:       SampleMaps,
cmap:       Cmap ¬ NIL,
placementButton:   Buttons.Button ¬ NIL,
placement:     {perAIS, none, center} ¬ perAIS,
clearInBetweenButton:  Buttons.Button ¬ NIL,
clearInBetween:    BOOL ¬ FALSE,
filenames, currentName: LIST OF Gallery ¬ NIL
];
ctGalleryUsage: ROPE ~ "Ct Gallery {pattern and/or list of <filename [-cmap <cmap>]}.";
CtGallery: CtProc ~ {
FirstChar: PROC [rope: ROPE, char: CHAR] RETURNS [BOOL] ~ {
RETURN[rope.Length[] > 0 AND Rope.Fetch[rope, 0] = char];
};
LastChar: PROC [rope: ROPE, char: CHAR] RETURNS [BOOL] ~ {
ropeLength: INT ¬ Rope.Length[rope];
RETURN[ropeLength > 0 AND Rope.Fetch[rope, ropeLength-1] = char];
};
RopeShorten: PROC [rope: ROPE, n: INT] RETURNS [ROPE] ~ {
ropeLength: INT ¬ Rope.Length[rope];
IF ropeLength >= n THEN rope ¬ Rope.Substr[rope, 0, ropeLength-n];
RETURN[rope];
};
i: NAT ¬ 1;
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
g: REF GalleryData ¬ NEW[GalleryData ¬ [cmd: cmd, image: viewer, maps: maps, wDir: wDir]];
affect ¬ [[0, 0], [0, 0]];
WHILE i < args.argc DO
IF NOT FirstChar[args[i], '-]
THEN {
EachName: PROC [fullFName: ROPE] RETURNS [continue: BOOL ¬ TRUE] ~ {
Process.CheckForAbort[];
IF CtFile.Parse[fullFName].type = bad
THEN IO.PutF1[cmd.out, "Bad argument: %g\n", IO.rope[args[i]]]
ELSE g.filenames ¬ CONS[[fullFName], g.filenames];
};
name: ROPE ¬ FS.ExpandName[FileNames.ResolveRelativePath[args[i]]].fullFName;
IF Rope.Find[name, "*"] = -1
THEN [] ¬ EachName[name]
ELSE FS.EnumerateForNames[name, EachName ! FS.Error => CONTINUE];
}
ELSE IF Rope.Equal[args[i], "-cmap", FALSE] AND g.filenames # NIL
THEN g.filenames.first.cmap ¬ FileNames.ResolveRelativePath[args[i ¬ i+1]];
i ¬ i+1;
ENDLOOP;
IF g.filenames = NIL
THEN IO.PutRope[cmd.out, "No such files"]
ELSE TRUSTED {Process.Detach[FORK MakeGalleryViewer[g]]};
};
MakeGalleryViewer: PROC [g: REF GalleryData] ~ {
Reverse: PROC [list: LIST OF Gallery] RETURNS [reverse: LIST OF Gallery] ~ {
FOR l: LIST OF Gallery ¬ list, l.rest WHILE l # NIL DO
reverse ¬ CONS[l.first, reverse];
ENDLOOP;
};
g.filenames ¬ Reverse[g.filenames];
g.parent ¬ Containers.Create[info: [name: "Ct Gallery", openHeight: 73, iconic: TRUE,
column: right, scrollable: FALSE], paint: FALSE];
g.cmap ¬ CtMap.Read[];
g.currentName ¬ g.filenames;
[] ¬ Buttons.Create[info: [parent: g.parent, name: "\t\t\t\t\t\t\tNEXT PICTURE", wx: 5, wy: 5, ww: 273, wh: 50], proc: Next, clientData: g, paint: TRUE];
g.clearInBetweenButton ¬ Buttons.Create[
info: [parent: g.parent, name: "Clear In Between: Off", wx: 283, wy: 40],
proc: ClearInBetween, clientData: g, paint: TRUE];
g.placementButton ¬ Buttons.Create[
info: [parent: g.parent, name: "Placement: Per AIS", wx: 283, wy: 20],
proc: PlacementButton, clientData: g, paint: TRUE];
ViewerOps.OpenIcon[g.parent];
g.ctView ¬ CtDispatch.GetCtOp["View"].proc;
};
Next: ButtonProc ~ {
Blink: PROC [rope: ROPE] ~ {
MessageWindow.Append[Rope.Concat["\t\t", rope], TRUE];
MessageWindow.Blink[];
};
g: REF GalleryData ¬ NARROW[clientData];
err: ROPE;
IF g.currentName = NIL THEN RETURN;
IF g.clearInBetween THEN CtBasic.FillMaps[g.maps];
g.cmd.commandLine ¬ Rope.Cat[g.currentName.first.aisName, " ",
SELECT g.placement FROM none => "-noPlace\n", center => "-center\n", ENDCASE => "\n"];
MessageWindow.Append[g.currentName.first.aisName, TRUE];
IF g.currentName = g.filenames THEN MessageWindow.Append[" (. . . first)"];
IF g.currentName.first.cmap = NIL
THEN CtMap.Write[g.cmap]
ELSE IF NOT CtMap.Load[g.currentName.first.cmap] THEN Blink["Bad color map"];
IF (err ¬ g.ctView[g.image, g.maps, NIL, g.cmd, g.wDir].error) # NIL THEN Blink[err];
IF (g.currentName ¬ g.currentName.rest) = NIL THEN {
g.currentName ¬ g.filenames;
MessageWindow.Append[" (. . . last)"];
};
};
PlacementButton: ButtonProc ~ {
g: REF GalleryData ¬ NARROW[clientData];
new: ROPE ¬ Rope.Concat["Placement: ", SELECT g.placement FROM
perAIS => "None", center => "Per AIS", ENDCASE => "Center"];
g.placement ¬ SELECT g.placement FROM perAIS=> none, center=> perAIS, ENDCASE=> center;
Buttons.ReLabel[g.placementButton, new];
};
ClearInBetween: ButtonProc ~ {
g: REF GalleryData ¬ NARROW[clientData];
new: ROPE ¬ Rope.Concat["Clear In Between: ", IF g.clearInBetween THEN "Off" ELSE "On"];
g.clearInBetween ¬ NOT g.clearInBetween;
Buttons.ReLabel[g.clearInBetweenButton, new];
};
Movie Command
MovieData:  TYPE ~ REF MovieDataRec;
MovieDataRec: TYPE ~ MONITORED RECORD [
cmd:       Commander.Handle ¬ NIL,
x, y, w, h, nFrames:  INTEGER ¬ 0,
nRows, nCols:    INTEGER ¬ 0,
speed, frame:     Controls.Control ¬ NIL,
outer:       Controls.Viewer ¬ NIL,
outerData:     Controls.OuterData ¬ NIL,
maps, frames, save:   SampleMaps,
now:       REAL ¬ 0.0,
old, new:      INTEGER ¬ 0,
pause, pauseSave:   BOOL ¬ FALSE,
bounce:      BOOL ¬ TRUE,
forward, forwardSave:  BOOL ¬ TRUE
];
ctMovieUsage: ROPE ~ "Ct Movie <x y w h nCols nRows> [-name <fileName>] [-clear].";
CtMovie: CtProc ~ {
x, y, w, h, nCols, nRows, clear, name: Args.Arg;
[x, y, w, h, nCols, nRows, clear, name] ¬ Args.ArgsGet[cmd, "%iiiiii-clear%b-name%s" !
Args.Error => {error ¬ reason; CONTINUE}];
affect ¬ [[0, 0], [0, 0]];
IF error # NIL
THEN RETURN
ELSE {
m: MovieData ¬ NEW[MovieDataRec ¬ [
cmd: cmd,
maps: maps,
x: x.int, y: y.int,
w: w.int, h: h.int,
nCols: nCols.int, nRows: nRows.int,
nFrames: nCols.int*nRows.int]];
min: SF.Vec ¬ [m.y+m.h*(m.nRows-1)/2, m.x+m.w*(m.nCols-1)/2];
m.save ¬ CtBasic.CopyOfMaps[maps, min.f, min.s, m.w, m.h];
IF name.ok
THEN {
m.frames ¬ CtFile.ReadFile[name.rope];
IF m.frames.bpp = 0 THEN RETURN[error: "can't open named file."];
}
ELSE m.frames ¬ CtBasic.CopyOfMaps[maps, maps.x, maps.y, maps.w, maps.h];
IF m.frames.bpp=0 OR m.frames.w=0 OR m.frames.h=0 THEN RETURN[error: "no image"];
IF clear.ok AND clear.bool THEN CtBasic.FillMaps[maps];
TRUSTED {Process.Detach[FORK MakeMovieViewer[m]]};
};
};
MakeMovieViewer: PROC [m: MovieData] ~ {
m.outerData ¬ Controls.OuterViewer[
clientData: m,
column: right,
name: "Ct Movie",
destroyProc: MovieDestroy,
controls: LIST[
m.speed ¬ Controls.NewControl["Speed", hSlider, m,, 5.0, 0.1,,,,,,,,,,,, exp],
m.frame ¬ Controls.NewControl["Frame", hSlider, m, 0, m.nFrames-1,0, Frame,, 0]],
buttons: LIST [
Controls.ClickButton["Forward", Forward, m],
Controls.ClickButton["Bounce-On", Bounce, m],
Controls.ClickButton["Pause", Pause, m]]
];
[] ¬ CedarProcess.Fork[Movie, m, [background, TRUE]];
};
MovieDestroy: Controls.DestroyProc ~ {NARROW[clientData, MovieData].pause ¬ FALSE};
ShowNFrame: PROC [m: MovieData] ~ {
SetNFrame[m];
IF m.new # m.old THEN {
min: SF.Vec ¬ [m.y+(m.new/m.nCols)*m.h, (m.new MOD m.nCols)*m.w];
CtBasic.CopyClippedMaps[m.frames, m.maps, [min, [min.s+m.h, min.f+m.w]], m.save.box];
m.old ¬ m.new;
IF NOT m.pause THEN Controls.SetSliderDialValue[m.frame, m.new];
};
};
PrintBox: PROC [out: IO.STREAM, name: Rope.ROPE, box: SF.Box] ~ {
IO.PutFL[out, "%g: [%g, %g] to [%g, %g]\n", LIST[IO.rope[name], IO.int[box.min.s], IO.int[box.min.f], IO.int[box.max.s], IO.int[box.max.f]]];
};
SetNFrame: PROC [m: MovieData] ~ {
IF (m.new ¬ Real.Round[m.now]) IN [0..m.nFrames) THEN RETURN;
IF m.bounce
THEN {
m.forward ¬ NOT m.forward;
m.new ¬ IF m.new < 0 THEN 0 ELSE m.nFrames-1;
}
ELSE {
IF m.new >= m.nFrames THEN m.now ¬ m.new ¬ 0;
IF m.new < 0 THEN m.now ¬ m.new ¬ m.nFrames-1;
};
};
Movie: CedarProcess.ForkableProc ~ {
m: MovieData ¬ NARROW[data];
WHILE NOT m.outerData.destroyed DO
IF m.pause THEN {Process.Pause[Process.MsecToTicks[150]]; LOOP};
m.now ¬ m.now+(IF m.forward THEN m.speed.value ELSE -m.speed.value);
ShowNFrame[m];
ENDLOOP;
CtBasic.CopyMaps[m.save, m.maps];
};
Frame: Controls.ControlProc ~ {
m: MovieData ~ NARROW[control.clientData];
IF control.mouse.state = down AND NOT m.pause THEN {
m.pauseSave ¬ FALSE;
m.pause ¬ TRUE;
PauseStyle[m];
};
m.now ¬ control.value;
ShowNFrame[m];
IF control.mouse.state = up AND NOT m.pauseSave THEN {
m.pause ¬ FALSE;
PauseStyle[m];
};
};
Pause: Controls.ClickProc ~ {
m: MovieData ~ NARROW[clientData];
m.pause ¬ m.pauseSave ¬ NOT m.pause;
PauseStyle[m];
};
PauseStyle: PROC [m: MovieData] ~ {
Controls.ButtonStyle[
m.outerData, "Pause", IF m.pause THEN $WhiteOnBlack ELSE $BlackOnWhite];
};
Bounce: Controls.ClickProc ~ {
m: MovieData ~ NARROW[clientData];
Controls.ButtonToggle[m.outerData, m.bounce ¬ NOT m.bounce, "Bounce-On", "Bounce-Off"];
IF m.bounce THEN m.forwardSave ¬ m.forward ELSE m.forward ¬ m.forwardSave;
};
Forward: Controls.ClickProc ~ {
m: MovieData ~ NARROW[clientData];
m.forward ¬ m.forwardSave ¬ NOT m.forward;
Controls.ButtonToggle[m.outerData, m.forward, "Forward", "Reverse"];
};
Start Code
CtDispatch.RegisterCtOp["Movie and Gallery:", NIL,   NIL];
CtDispatch.RegisterCtOp["Gallery",    CtGallery, ctGalleryUsage];
CtDispatch.RegisterCtOp["Movie",     CtMovie,  ctMovieUsage];
END.