<> <> <> 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]; <> 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 = { <> 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; <> [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 { <> 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 { <> 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 { <> 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]; <> <> }; DuplicateSelButton: Buttons.ButtonProc ~ { toolData: ToolData _ NARROW[clientData]; <> <> }; TransposeButton: Buttons.ButtonProc ~ { toolData: ToolData _ NARROW[clientData]; <> <> }; MakeHeaderButton: Buttons.ButtonProc ~ { toolData: ToolData _ NARROW[clientData]; <> <> }; AppendButton: Buttons.ButtonProc ~ { toolData: ToolData _ NARROW[clientData]; <> <> }; 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; <> IF toolData.pleaseStop THEN RETURN; <> <> <> <> <<[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 { <> 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 => { <> < RowRootOf[selectedBox, TRUE],>> < ColRootOf[selectedBox, TRUE],>> < ERROR;>> <> <> <> <> <<};>> }; 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 => { <> }; row => { <> <> <> <> <<}>> <> <> <> <<};>> }; column => { <> <> <> <> <<}>> <> <> <> <<};>> }; 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 => { <> <> }; column => { <> <> }; ENDCASE => ERROR; TakeDownSelection[toolData]; <> <> <> <<}>> <> <> <> <<};>> <> 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; <> < RowRootOf[selectedBox, TRUE],>> < ColRootOf[selectedBox, TRUE],>> < ERROR;>> <> <> TakeDownSelection[toolData]; <> <>> <> <>> <> < GOTO Blink;>> RefreshTableDisplay[toolData]; }; }; DoAppendRowOrCol: PROCEDURE [toolData: ToolData, selectedBox: RefTableBox] = { IF selectedBox # NIL AND NOT TableSelection.AnEmptySelection[toolData.selection] THEN { TakeDownSelection[toolData]; <