TableToolImpl.Mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Rick Beach, February 19, 1985 10:39:32 am PST
DIRECTORY
Buttons, FS, Commander, Containers, EditSpan, EditSpanSupport, Graphics, InputFocus, Labels, Menus, NodeProps, NodeStyle, Process, PutGet, Rope, Rules, TableBase, TableOps, TableSelection, TEditSelection, TextNode, TiogaOps, TiogaOpsDefs, TIPUser, TSJaMPageBuilder, TSObject, TSOutput, TSOutputDisplay, TSTranslate, TSTypes, ViewerClasses, ViewerOps, ViewerTools;
TableToolImpl: CEDAR PROGRAM
IMPORTS Buttons, FS, Commander, Containers, EditSpan, EditSpanSupport, InputFocus, Graphics, Labels, Menus, NodeProps, Process, PutGet, Rope, Rules, TableBase, TableOps, TableSelection, TEditSelection, TextNode, TiogaOps, TIPUser, TSJaMPageBuilder, TSTranslate, TSTypes, ViewerOps, ViewerTools =
BEGIN
OPEN TableBase;
ROPE: TYPE = Rope.ROPE;
indent: INTEGER ~ 4;
baseline: INTEGER ~ 16;
ToolData: TYPE = REF ToolDataRec;
ToolDataRec: TYPE = RECORD [
nameViewer: ViewerClasses.Viewer ← NIL,
msgViewer: ViewerClasses.Viewer ← NIL,
msgRope: ROPENIL,
displayViewer: ViewerClasses.Viewer ← NIL,
selection: TableSelection.Selection ← NIL,
selectingRowOrColumn: TableSelection.KindOfSelection ← row,
callBackProc: DisplayProc ← NIL,
entry: RefTableEntry ← NIL,
beforeButton: Buttons.Button,
afterButton: Buttons.Button,
rowButton: Buttons.Button,
colButton: Buttons.Button,
rootOfTable: TextNode.Ref,
galley: TSObject.ItemList,
style: NodeStyle.Ref,
pleaseStop: BOOLEANFALSE
];
DisplayProc: TYPE = PROCEDURE [dc: Graphics.Context, toolData: ToolData];
TableToolInitProc: ViewerClasses.InitProc = {
toolData: ToolData ← NEW[ToolDataRec];
nextX: INTEGER;
curY: INTEGER ← 0;
NextY: PROCEDURE RETURNS [y:NAT] = {y ← curY ← curY+baseline};
button: ViewerClasses.Viewer;
rule: Rules.Rule;
menu: Menus.Menu ← Menus.CreateMenu[];
InsertMenu: PROCEDURE [name: ROPE, proc: Menus.ClickProc, doc: ROPE] = {
Menus.InsertMenuEntry[menu, Menus.CreateEntry[
name: name,
proc: proc,
clientData: toolData,
documentation: doc
]];
};
LabelledTextViewer: PROCEDURE [name: ROPE] RETURNS [v: ViewerClasses.Viewer] = {
v ← ViewerTools.MakeNewTextViewer[
info:[wx: button.wx+button.ww, wy: curY, ww: 50, wh: button.wh,
parent: self, scrollable: FALSE, border: FALSE],
paint: TRUE
];
button ← Labels.Create[
info: [name: name, wx: v.wx + v.ww, wy: curY+1, wh: button.wh, parent: self, scrollable: FALSE, border: FALSE],
paint: TRUE
];
};
MyButton: PROCEDURE [name: ROPE, x, y: NAT, proc: Buttons.ButtonProc, doc: ROPE ← NIL] RETURNS [Buttons.Button] = {
RETURN [Buttons.Create[
info: [name: name, wx: x, wy: y, parent: self, border: FALSE],
clientData: toolData,
proc: proc,
fork: TRUE,
paint: TRUE,
documentation: doc
]]};
ViewerOps.AddProp[self, $TableToolData, toolData];
containerInitProc[self]; -- since I know its really a $Container, I need to init it for him
InsertMenu["DisplayNextTable", DisplayNextTableButton, "Displays the next table found in the document"];
InsertMenu["DisplaySelectedTable", DisplaySelectedTableButton, "Displays the table containing the selection"];
InsertMenu["DisplayDocument", DisplayDocumentButton, "Displays the document"];
InsertMenu["ABORT!", AbortButton, "Aborts the formatting process"];
ViewerOps.SetMenu[self, menu];
toolData.beforeButton ← MyButton[name: "Before ", x: indent, y: curY, proc: BeforeButton];
toolData.afterButton ← MyButton[name: "After ", x: toolData.beforeButton.wx+toolData.beforeButton.ww, y: curY, proc: AfterButton];
rule ← Rules.Create[info: [parent: self, wx: toolData.afterButton.wx+toolData.afterButton.ww, wy: curY, ww: 1, wh: toolData.afterButton.wh+1]];
toolData.rowButton ← MyButton[name: "Row ", x: rule.wx+rule.ww, y: curY, proc: RowButton];
toolData.colButton ← MyButton[name: "Col ", x: toolData.rowButton.wx+toolData.rowButton.ww, y: curY, proc: ColButton];
rule ← Rules.Create[info: [parent: self, wx: toolData.colButton.wx+toolData.colButton.ww, wy: curY, ww: 1, wh: toolData.colButton.wh+1]];
button ← MyButton[name: "Delete ", x: rule.wx+rule.ww, y: curY, proc: DeleteSelButton];
button ← MyButton[name: "Duplicate ", x: button.wx+button.ww, y: curY, proc: DuplicateSelButton];
button ← MyButton[name: "MakeHeader ", x: button.wx+button.ww, y: curY, proc: MakeHeaderButton];
button ← MyButton[name: "Append ", x: button.wx+button.ww, y: curY, proc: AppendButton];
button ← MyButton[name: "Transpose ", x: button.wx+button.ww, y: curY, proc: TransposeButton];
rule ← Rules.Create[info: [parent: self, wx: 0, wy: NextY[], ww: 999, wh: 1]];
Containers.ChildXBound[self, rule];
curY ← curY + 2;
button ← MyButton[name: "DocumentName: ", x: indent, y: curY, proc: DocumentNameButton, doc: "Left-click to get selected document name; Middle-click to make current name pending-delete; Right-click to clear the name viewer."];
toolData.nameViewer ← ViewerTools.MakeNewTextViewer[
info:[wx: button.wx+button.ww, wy: curY+1, ww: 50, wh: baseline,
parent: self, scrollable: TRUE, border: FALSE],
paint: TRUE
];
Containers.ChildXBound[self, toolData.nameViewer];
rule ← Rules.Create[info: [parent: self, wx: 0, wy: NextY[]+1, ww: 999, wh: 1]];
Containers.ChildXBound[self, rule];
curY ← curY + 2;
toolData.msgViewer ← ViewerTools.MakeNewTextViewer[
info:[wx: 0, wy: curY, ww: 50, wh: baseline,
parent: self, scrollable: TRUE, border: FALSE],
paint: TRUE
];
Containers.ChildXBound[self, toolData.msgViewer];
rule ← Rules.Create[info: [parent: self, wx: 0, wy: NextY[], ww: 999, wh: 1]];
Containers.ChildXBound[self, rule];
curY ← curY + 2;
toolData.displayViewer ← ViewerOps.CreateViewer[
flavor: $TSDisplay,
info: [
wx: 0, wy: curY, ww: 50, wh: baseline,
border: FALSE,
parent: self,
scrollable: FALSE],
paint: TRUE];
IF originalTSDisplayPaintProc = NIL THEN
originalTSDisplayPaintProc ← toolData.displayViewer.class.paint;
Containers.ChildXBound[self, toolData.displayViewer];
Containers.ChildYBound[self, toolData.displayViewer];
};
TableToolDestroyProc: ViewerClasses.DestroyProc = {
toolData: ToolData ~ ToolDataFromViewer[self];
IF toolData # NIL AND toolData.selection # NIL AND toolData.selection.table # NIL THEN {
BreakTableLinks[toolData.selection.table];
toolData.selection ← NIL;
};
IF containerDestroyProc # NIL THEN containerDestroyProc[self];
};
TableToolPaintProc: ViewerClasses.PaintProc = {
PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL];
IF containerPaintProc # NIL THEN containerPaintProc[self, context, whatChanged, clear];
IF whatChanged = NIL AND clear THEN {
toolData: ToolData ← ToolDataFromViewer[self];
IF toolData # NIL AND toolData.galley # NIL THEN
TRUSTED { Process.Detach[FORK DisplayTable[toolData]]; };
};
};
AbortButton: Menus.ClickProc = {
toolData: ToolData ~ NARROW[clientData];
toolData.pleaseStop ← TRUE;
};
DisplayDocumentButton: Menus.ClickProc = {
toolData: ToolData ~ NARROW[clientData];
docName: ROPE ~ ViewerTools.GetContents[toolData.nameViewer];
v: ViewerClasses.Viewer ~ ViewerOps.FindViewer[docName];
errorMsg: ROPENIL;
root: TextNode.Ref;
toolData.pleaseStop ← FALSE;
IF v # NIL THEN
TRUSTED { root ← LOOPHOLE[TiogaOps.ViewerDoc[v]] }
ELSE
root ← PutGet.FromFile[docName ! FS.Error => {errorMsg ← error.explanation; CONTINUE}];
IF errorMsg # NIL THEN
AppendLogMessage[toolData, Rope.Cat["Document \"", docName, "\" was not found for the reason: ", errorMsg]]
ELSE IF root = NIL THEN
AppendLogMessage[toolData, Rope.Cat["Viewer \"", docName, "\" wasn't found."]]
ELSE {
FormatTableFromRoot[toolData, root];
DisplayTable[toolData];
};
};
DisplaySelectedTableButton: Menus.ClickProc = {
toolData: ToolData ~ NARROW[clientData];
v: ViewerClasses.Viewer;
start: TiogaOps.Location;
toolData.pleaseStop ← FALSE;
get node from selection
[v, start] ← TiogaOps.GetSelection[primary];
IF v = NIL THEN
AppendLogMessage[toolData, "No primary selection for a selected table."]
ELSE TRUSTED {
node: TextNode.Ref ← LOOPHOLE[start.node];
WHILE node # NIL AND NOT ATableNode[node] DO
node ← TextNode.Parent[node];
ENDLOOP;
IF node = NIL THEN
AppendLogMessage[toolData, "Selected node is not in a table with ArtworkClass Table."]
ELSE {
FormatTableFromRoot[toolData, node];
DisplayTable[toolData];
ViewerTools.SetContents[toolData.nameViewer, v.name];
};
};
};
ATableNode: PROC [node: TextNode.Ref] RETURNS [BOOLEAN] ~ {
IF node = NIL THEN RETURN [FALSE]
ELSE {
artworkClass: ROPENARROW[NodeProps.GetProp[node, $ArtworkClass]];
RETURN[artworkClass.Equal["Table"]];
};
};
DisplayNextTableButton: Menus.ClickProc = {
toolData: ToolData ~ NARROW[clientData];
toolData.pleaseStop ← FALSE;
IF toolData.rootOfTable = NIL THEN
AppendLogMessage[toolData, "No table previously displayed so I can't find the next one."]
ELSE {
search from rootOfTable to end of document for the next table
node: TextNode.Ref ← TextNode.Forward[toolData.rootOfTable].nx;
WHILE node # NIL AND NOT ATableNode[node] DO
node ← TextNode.Forward[node].nx;
ENDLOOP;
IF node = NIL THEN {
search from the root of the document until rootOfTable for the next table
node ← TextNode.Root[toolData.rootOfTable];
WHILE node # NIL AND node # toolData.rootOfTable AND NOT ATableNode[node] DO
node ← TextNode.Forward[node].nx;
ENDLOOP;
};
IF node = NIL OR node = toolData.rootOfTable THEN
AppendLogMessage[toolData, "Sorry, no other table found in document."]
ELSE {
we have another table, so format and display it
FormatTableFromRoot[toolData, node];
DisplayTable[toolData];
};
};
};
DocumentNameButton: Buttons.ButtonProc = {
toolData: ToolData ← NARROW[clientData];
IF mouseButton = red THEN {
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
sourceName: ROPE
IF selectedViewer = NIL OR selectedViewer.class.get = NIL THEN NIL
ELSE NARROW[selectedViewer.class.get[selectedViewer, $SelChars]];
IF sourceName.Length <= 1 THEN {
IF (selectedViewer = NIL) THEN {
AppendLogMessage[toolData, "Selection not in text viewer"];
RETURN;
}
ELSE {
WHILE sourceName.Length <= 1 AND selectedViewer # NIL DO
sourceName ← selectedViewer.name;
selectedViewer ← selectedViewer.parent;
ENDLOOP;
};
};
IF sourceName.Length > 1 THEN {
selectedViewer ← ViewerOps.FindViewer[sourceName];
IF selectedViewer = NIL THEN
AppendLogMessage[toolData, Rope.Cat["Viewer \"", sourceName, "\" wasn't found."]]
ELSE {
ViewerTools.SetContents[toolData.nameViewer, sourceName];
};
};
}
ELSE {
ViewerTools.SetContents[toolData.nameViewer, ""];
};
ViewerTools.SetSelection[toolData.nameViewer];
};
BeforeButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF toolData.selection # NIL THEN {
toolData.selection.insertion ← before;
Buttons.SetDisplayStyle[toolData.beforeButton, $WhiteOnBlack];
Buttons.SetDisplayStyle[toolData.afterButton, $BlackOnWhite];
};
};
AfterButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF toolData.selection # NIL THEN {
toolData.selection.insertion ← after;
Buttons.SetDisplayStyle[toolData.beforeButton, $BlackOnWhite];
Buttons.SetDisplayStyle[toolData.afterButton, $WhiteOnBlack];
};
};
RowButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
toolData.selectingRowOrColumn ← row;
Buttons.SetDisplayStyle[toolData.rowButton, $WhiteOnBlack];
Buttons.SetDisplayStyle[toolData.colButton, $BlackOnWhite];
};
ColButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
toolData.selectingRowOrColumn ← column;
Buttons.SetDisplayStyle[toolData.rowButton, $BlackOnWhite];
Buttons.SetDisplayStyle[toolData.colButton, $WhiteOnBlack];
};
DeleteSelButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
DoDeleteSelection[toolData, toolData.selection.startBox, toolData.selection.kind];
};
DuplicateSelButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
DoDuplicateRowOrCol[toolData, toolData.selection.startBox, toolData.selection.kind];
};
TransposeButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
DoTransposeTable[toolData, toolData.selection.startBox];
};
MakeHeaderButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
DoMakeRowOrColHeader[toolData, toolData.selection.startBox, toolData.selection.kind];
};
AppendButton: Buttons.ButtonProc ~ {
toolData: ToolData ← NARROW[clientData];
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
DoAppendRowOrCol[toolData, toolData.selection.startBox, toolData.selection.kind];
};
ToolDataFromViewer: PROCEDURE [v: ViewerClasses.Viewer] RETURNS [toolData: ToolData] = {
IF v # NIL THEN RETURN [NARROW[ViewerOps.FetchProp[v, $TableToolData]]];
};
TableFromViewer: PROCEDURE [v: ViewerClasses.Viewer] RETURNS [table: RefTable ← NIL] = {
r: REF ANY ~ ViewerOps.FetchProp[v, $TableRef];
IF r # NIL AND ISTYPE[r, RefTable] THEN
table ← NARROW[r, RefTable];
};
OutputHandleForTool: PROCEDURE [toolData: ToolData] RETURNS [handle: TSOutput.Handle]= {
IF toolData # NIL AND toolData.displayViewer # NIL THEN
handle ← NARROW[ViewerOps.FetchProp[toolData.displayViewer, $TSDisplayHandle]];
};
FormatTableFromRoot: PROC [toolData: ToolData, rootOfTable: TextNode.Ref] ~ {
ENABLE UNWIND => toolData.pleaseStop ← TRUE;
copyRoot: TextNode.Ref;
IF toolData.pleaseStop THEN RETURN;
copyRoot ← TextNode.Root[ -- copy the subtree for the slow TSetter to avoid locking issues
EditSpanSupport.CopySpan[ -- adds an extra root node
TextNode.MakeNodeSpan[rootOfTable, TextNode.LastWithin[rootOfTable]]].start.node];
copyRoot ← TextNode.FirstChild[copyRoot];
[toolData.galley, toolData.style] ← TSTranslate.TreeToVlist[copyRoot];
[toolData.galley, toolData.style] ← TSTranslate.TreeToVlist[rootOfTable];
toolData.rootOfTable ← rootOfTable;
};
DisplayTable: PROCEDURE [toolData: ToolData] = {
IF toolData.pleaseStop THEN RETURN;
UnHookNotifier[toolData];
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN TakeDownSelection[toolData];
DisplayFormattedTable[toolData];
toolData.selection ← TableSelection.NewSelection[TableFromViewer[toolData.displayViewer]];
HookUpNotifier[toolData];
};
DisplayFormattedTable: PROCEDURE [toolData: ToolData] = {
aborted: BOOLEANFALSE;
isAborted: PROC RETURNS [BOOLEAN] = { RETURN[toolData.pleaseStop] };
handle: TSOutput.Handle ~ OutputHandleForTool[toolData];
aborted ← TSJaMPageBuilder.RunPageBuilder[
galley: toolData.galley,
style: toolData.style,
output: handle,
abortCheckProc: isAborted,
documentName: NIL
];
handle.finishProc[handle];
toolData.pleaseStop ← toolData.pleaseStop OR aborted;
};
AppendLogMessage: PROCEDURE [toolData: ToolData, msg: ROPE] = {
toolData.msgRope ← toolData.msgRope.Cat[msg, "\n"];
ViewerTools.SetContents[toolData.msgViewer, toolData.msgRope];
[] ← toolData.msgViewer.class.scroll[toolData.msgViewer, thumb, 100];
TEditSelection.InvalidateLineCache[];
};
HookUpNotifier: PROCEDURE [toolData: ToolData] = {
v: ViewerClasses.Viewer ~ toolData.displayViewer;
IF v # NIL THEN {
v.class.tipTable ← tableToolTIPTable;
v.tipTable ← tableToolTIPTable;
v.class.notify ← TableToolNotifier;
v.class.paint ← TableToolTSDisplayPaintProc;
};
};
UnHookNotifier: PROCEDURE [toolData: ToolData] = {
v: ViewerClasses.Viewer ~ toolData.displayViewer;
IF v # NIL THEN {
v.class.notify ← NoOpNotifier;
v.class.paint ← originalTSDisplayPaintProc;
};
};
NoOpNotifier: ViewerClasses.NotifyProc = { RETURN; };
TableToolNotifier: ViewerClasses.NotifyProc = {
toolData: ToolData ← ToolDataFromViewer[self.parent];
{
mouse: TIPUser.TIPScreenCoords;
selectedBox: RefTableBox;
InputFocus.SetInputFocus[toolData.displayViewer];
FOR list: LIST OF REF ANY ← input, list.rest UNTIL list = NIL DO
WITH list.first SELECT FROM
x: ATOM => SELECT x FROM
$InsertBefore => {
toolData.selection.insertion ← before;
};
$InsertAfter => {
toolData.selection.insertion ← after;
};
$Row => {
toolData.selectingRowOrColumn ← row;
};
$Column => {
toolData.selectingRowOrColumn ← column;
};
$SelBox => {
DoSelectBox[toolData, selectedBox];
};
$SelRow => {
DoSelectRowOrCol[toolData, selectedBox, row];
};
$SelCol => {
DoSelectRowOrCol[toolData, selectedBox, column ! CouldNotDoIt => GOTO Blink];
};
$SelUpdate => {
DoSelectUpdate[toolData, selectedBox];
};
$SelExpand => {
DoSelectExpand[toolData, selectedBox];
};
$SelReduce => {
DoSelectReduce[toolData, selectedBox];
};
$SelExtend => {
DoSelectExtend[toolData, selectedBox];
};
$DuplicateRowOrCol => {
DoDuplicateRowOrCol[toolData, selectedBox];
};
$DeleteSelection => {
DoDeleteSelection[toolData, selectedBox];
};
$TransposeTable => {
DoTransposeTable[toolData, selectedBox];
};
$MakeRowOrColHeader => {
DoMakeRowOrColHeader[toolData, selectedBox];
};
$AppendRowOrCol => {
DoAppendRowOrCol[toolData, selectedBox];
};
ENDCASE => NULL;
z: TIPUser.TIPScreenCoords => {
mouse ← z;
selectedBox ← ResolveMouseToBox[toolData, mouse];
};
ENDCASE => ERROR;
ENDLOOP;
EXITS
Blink => ViewerOps.BlinkIcon[toolData.displayViewer];
};
};
CouldNotDoIt: SIGNAL = CODE;
DoSelectBox: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL THEN {
TakeDownSelection[toolData];
toolData.selection.list ← LIST[NEW[TableSelection.SelectedThingRec ← [box~selectedBox]]];
toolData.selection.kind ← box;
PutUpSelection[toolData];
};
};
DoSelectRowOrCol: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox,
which: TableSelection.KindOfSelection] = {
IF selectedBox # NIL THEN {
IF which # row AND which # column THEN SIGNAL CouldNotDoIt;
IF toolData.selection.kind # which --OR NOT TableSelection.WithinSelection[toolData.selection, selectedBox]-- THEN {
Update the selection
TakeDownSelection[toolData];
TableSelection.GrowSelection[toolData.selection, IF which = row THEN row ELSE column];
PutUpSelection[toolData];
};
};
};
DoSelectUpdate: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND TableSelection.ASingleSelection[toolData.selection] THEN {
SELECT toolData.selection.kind FROM
box => {
IF toolData.selection.list.first.box # selectedBox THEN {
TakeDownSelection[toolData];
toolData.selection.list ← LIST[NEW[TableSelection.SelectedThingRec ← [box~selectedBox]]];
PutUpSelection[toolData];
};
};
row, column => {
selectedBox ← SELECT toolData.selection.kind FROM
row => RowRootOf[selectedBox, TRUE],
column => ColRootOf[selectedBox, TRUE],
ENDCASE => ERROR;
IF toolData.selection.startBox # selectedBox THEN {
TakeDownSelection[toolData];
toolData.selection.startBox ← toolData.selection.endBox ← selectedBox;
PutUpSelection[toolData];
};
};
ENDCASE => ERROR;
};
};
DoSelectExpand : PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
TakeDownSelection[toolData];
SELECT toolData.selection.kind FROM
box => TableSelection.GrowSelection[toolData.selection, row];
row => TableSelection.GrowSelection[toolData.selection, row];
column => TableSelection.GrowSelection[toolData.selection, column];
table => NULL;
ENDCASE => ERROR;
PutUpSelection[toolData];
};
};
DoSelectReduce: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
currentSelection: TableSelection.SelectionRec ~ toolData.selection^;
TakeDownSelection[toolData];
SELECT currentSelection.kind FROM
box => {
toolData.selection.startBox ← toolData.selection.endBox ← selectedBox;
};
row => {
selectedBox ← RowRootOf[selectedBox, TRUE];
IF BoxBeforeBox[selectedBox, currentSelection.startBox] THEN {
toolData.selection.startBox ← selectedBox;
toolData.selection.endBox ← currentSelection.endBox;
}
ELSE {
toolData.selection.startBox ← currentSelection.startBox;
toolData.selection.endBox ← selectedBox;
};
};
column => {
selectedBox ← ColRootOf[selectedBox, TRUE];
IF BoxBeforeBox[selectedBox, currentSelection.startBox] THEN {
toolData.selection.startBox ← selectedBox;
toolData.selection.endBox ← currentSelection.endBox;
}
ELSE {
toolData.selection.startBox ← currentSelection.startBox;
toolData.selection.endBox ← selectedBox;
};
};
ENDCASE => ERROR;
PutUpSelection[toolData];
};
};
DoSelectExtend: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
currentSelection: TableSelection.SelectionRec ~ toolData.selection^;
SELECT currentSelection.kind FROM
box => NULL;
row => {
selectedBox ← RowRootOf[selectedBox, TRUE];
IF NOT AreRowSiblings[selectedBox, currentSelection.startBox] THEN RETURN;
};
column => {
selectedBox ← ColRootOf[selectedBox, TRUE];
IF NOT AreColSiblings[selectedBox, currentSelection.startBox] THEN RETURN;
};
ENDCASE => ERROR;
TakeDownSelection[toolData];
IF BoxBeforeBox[selectedBox, currentSelection.startBox] THEN {
toolData.selection.startBox ← selectedBox;
toolData.selection.endBox ← currentSelection.endBox;
}
ELSE {
toolData.selection.startBox ← currentSelection.startBox;
toolData.selection.endBox ← selectedBox;
};
Could use optimization that paints incremental changes in the selection
PutUpSelection[toolData];
};
};
DoDeleteSelection: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
currentSelection: TableSelection.SelectionRec ~ toolData.selection^;
IF toolData.selectingRowOrColumn = box THEN GOTO Blink;
selectedBox ← SELECT toolData.selectingRowOrColumn FROM
row => RowRootOf[selectedBox, TRUE],
column => ColRootOf[selectedBox, TRUE],
ENDCASE => ERROR;
IF currentSelection.kind = box AND selectedBox # currentSelection.startBox THEN
GOTO Blink;
TakeDownSelection[toolData];
SELECT toolData.selection.kind FROM
row => TableOps.DeleteRowStructure[currentSelection.startBox];
column => TableOps.DeleteColStructure[currentSelection.startBox];
ENDCASE => GOTO Blink;
RefreshTableDisplay[toolData];
};
EXITS
Blink => ViewerOps.BlinkIcon[toolData.displayViewer];
};
DoDuplicateRowOrCol: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
currentSelection: TableSelection.SelectionRec ~ toolData.selection^;
IF currentSelection.kind = box THEN GOTO Blink;
selectedBox ← SELECT toolData.selectingRowOrColumn FROM
row => RowRootOf[selectedBox, TRUE],
column => ColRootOf[selectedBox, TRUE],
ENDCASE => ERROR;
TakeDownSelection[toolData];
SELECT currentSelection.kind FROM
row => {
IF AreRowSiblings[selectedBox, currentSelection.startBox] AND
currentSelection.startBox # FirstRowChild[currentSelection.table.tableRoot] THEN
TableOps.CopyRowStructure[currentSelection.startBox, selectedBox, currentSelection.insertion, TRUE]
ELSE GOTO Blink;
};
column => {
IF AreColSiblings[selectedBox, currentSelection.startBox] AND
currentSelection.startBox # FirstColChild[currentSelection.table.tableRoot] THEN
TableOps.CopyColStructure[currentSelection.startBox, selectedBox, currentSelection.insertion, TRUE]
ELSE GOTO Blink;
};
ENDCASE => GOTO Blink;
RefreshTableDisplay[toolData];
};
EXITS
Blink => ViewerOps.BlinkIcon[toolData.displayViewer];
};
DoTransposeTable: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
TakeDownSelection[toolData];
TableOps.TransposeTable[toolData.selection.table];
RefreshTableDisplay[toolData];
};
};
DoMakeRowOrColHeader: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] AND toolData.selection.kind = toolData.selectingRowOrColumn THEN {
currentSelection: TableSelection.SelectionRec ~ toolData.selection^;
TakeDownSelection[toolData];
SELECT toolData.selectingRowOrColumn FROM
row =>
TableOps.MakeRowHeader[currentSelection.table, currentSelection.startBox, currentSelection.endBox];
column =>
TableOps.MakeColHeader[currentSelection.table, currentSelection.startBox, currentSelection.endBox];
ENDCASE => GOTO Blink;
RefreshTableDisplay[toolData];
};
};
DoAppendRowOrCol: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = {
IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN {
TakeDownSelection[toolData];
SELECT toolData.selectingRowOrColumn FROM
row => TableOps.AppendRow[toolData.selection.table];
column => TableOps.AppendCol[toolData.selection.table];
ENDCASE => SIGNAL CouldNotDoIt;
RefreshTableDisplay[toolData];
};
};
RefreshTableDisplay: PROCEDURE [toolData: ToolData] = {
oldBranch, oldBranchRoot: TextNode.Ref;
newBranch: TextNode.Ref;
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN TakeDownSelection[toolData];
newBranch ← TableToBranch[toolData.selection.table];
oldBranch ← toolData.selection.table.branch;
oldBranchRoot ← TextNode.Root[oldBranch];
TRUSTED { TiogaOps.Lock[LOOPHOLE[oldBranchRoot]] };
[] ← EditSpan.MoveOnto[
destRoot: oldBranchRoot,
sourceRoot: TextNode.Root[newBranch],
dest: TextNode.MakeNodeSpan[oldBranch, TextNode.LastWithin[oldBranch]],
source: TextNode.MakeNodeSpan[newBranch, TextNode.LastWithin[newBranch]]];
TRUSTED { TiogaOps.Unlock[LOOPHOLE[oldBranchRoot]] };
BreakTableLinks[toolData.selection.table];
toolData.selection ← NIL;
DisplayTable[toolData];
};
BreakTableLinks: PROCEDURE [table: RefTable] = {
table.tableGrid ← NIL;
table.rowGridPositions ← NIL;
table.colGridPositions ← NIL;
};
ResolveMouseToBox: PROCEDURE [toolData: ToolData, mouse: TIPUser.TIPScreenCoords] RETURNS [box: RefTableBox ← NIL] = {
IF toolData.selection # NIL THEN {
handle: TSOutput.Handle ~ OutputHandleForTool[toolData];
table: RefTable ~ toolData.selection.table;
displayState: TSOutputDisplay.DisplayState ~ NARROW[handle.outputState];
box ← TableSelection.ResolveToBox[table: table, x: [mouse.mouseX - table.originX], y: [mouse.mouseY - (toolData.displayViewer.ch - displayState.pageHeight) - table.originY]];
};
};
BoxBeforeBox: PROCEDURE [first, second: RefTableBox] RETURNS [before: BOOLEAN] = {
RETURN [(first.x.SubDimn[first.boxExtents[left]] <= second.x.SubDimn[second.boxExtents[left]]) AND
(first.y.AddDimn[first.boxExtents[up]] >= second.y.AddDimn[second.boxExtents[up]])]
};
TakeDownSelection: PROCEDURE [toolData: ToolData] = {
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
PaintSelection[toolData];
toolData.selection.list ← NIL;
};
PutUpSelection: PROCEDURE [toolData: ToolData] = {
IF NOT TableSelection.AnEmptySelection[toolData.selection] THEN
PaintSelection[toolData];
};
PaintSelection: PROCEDURE [toolData: ToolData] = {
selection: TableSelection.Selection ~ toolData.selection;
PaintSelectedBox: EnumeratedEntryProc = {
toolData.entry ← entry;
Painter[InvertTableBox, toolData];
};
FOR list: LIST OF TableSelection.SelectedThing ← selection.list, list.rest WHILE list # NIL DO
SELECT selection.kind FROM
box => {
[] ← PaintSelectedBox[selection.table, list.first.box];
};
row => {
EnumerateTable[table~selection.table, entryProc~PaintSelectedBox, top~list.first.grid1, bottom~list.first.grid2];
};
column => {
EnumerateTable[table~selection.table, entryProc~PaintSelectedBox, left~list.first.grid1, right~list.first.grid2];
};
ENDCASE => ERROR;
ENDLOOP;
};
InvertTableBox: DisplayProc = {
PROCEDURE [dc: Graphics.Context, toolData: ToolData]
handle: TSOutput.Handle ~ OutputHandleForTool[toolData];
selection: TableSelection.Selection ~ toolData.selection;
table: RefTable ~ selection.table;
displayState: TSOutputDisplay.DisplayState ~ NARROW[handle.outputState];
xOffset: REAL ~ table.originX;
yOffset: REAL ~ table.originY + (toolData.displayViewer.ch - displayState.pageHeight);
oldPaintMode: Graphics.PaintMode ~ Graphics.SetPaintMode[dc, invert];
entry: RefTableEntry ~ toolData.entry;
llx: REAL ~ xOffset + table.colGridPositions[entry.left].texPts;
lly: REAL ~ yOffset + table.rowGridPositions[entry.bottom].texPts;
urx: REAL ~ xOffset + table.colGridPositions[entry.right].texPts;
ury: REAL ~ yOffset + table.rowGridPositions[entry.top].texPts;
Graphics.DrawBox[dc, [llx, lly, urx, ury]];
[] ← Graphics.SetPaintMode[dc, oldPaintMode];
};
Painter: PROCEDURE [proc: DisplayProc, toolData: ToolData] = {
toolData.callBackProc ← proc;
ViewerOps.PaintViewer[viewer: toolData.displayViewer, hint: client, whatChanged: toolData, clearClient: FALSE];
};
TableToolTSDisplayPaintProc: ViewerClasses.PaintProc = {
IF whatChanged # NIL AND ISTYPE[whatChanged, ToolData] THEN {
toolData: ToolData ~ NARROW[whatChanged, ToolData];
toolData.callBackProc[context, toolData];
}
ELSE IF originalTSDisplayPaintProc # NIL THEN
originalTSDisplayPaintProc[self, context, whatChanged, clear];
};
TableToolExecCommand: Commander.CommandProc = {
[] ← ViewerOps.CreateViewer[flavor: $TableTool, info: [name: "TableTool", scrollable: FALSE]]
};
originalTSDisplayPaintProc: ViewerClasses.PaintProc ← NIL;
tableToolTIPTable: TIPUser.TIPTable ← TIPUser.InstantiateNewTIPTable["TableTool.TIP"];
tableToolViewerClass: ViewerClasses.ViewerClass ~
NEW[ViewerClasses.ViewerClassRec ← ViewerOps.FetchViewerClass[$Container]^];
containerInitProc: ViewerClasses.InitProc ← tableToolViewerClass.init;
containerDestroyProc: ViewerClasses.DestroyProc ← tableToolViewerClass.destroy;
containerPaintProc: ViewerClasses.PaintProc ← tableToolViewerClass.paint;
tableToolViewerClass.init ← TableToolInitProc;
tableToolViewerClass.destroy ← TableToolDestroyProc;
tableToolViewerClass.paint ← TableToolPaintProc;
tableToolViewerClass.bltContents ← none;
tableToolViewerClass.tipTable ← tableToolTIPTable;
ViewerOps.RegisterViewerClass[$TableTool, tableToolViewerClass];
Commander.Register[key: "TableTool", proc: TableToolExecCommand, doc: "Create a table formatting tool"];
END.