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: ROPE ← NIL,
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: BOOLEAN ← FALSE
];
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: ROPE ← NIL;
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: ROPE ← NARROW[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: BOOLEAN ← FALSE;
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"];