IndexViewerImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by Rick Beach, July 12, 1983 4:34 pm
Rick Beach, February 27, 1985 9:53:25 pm PST
DIRECTORY
Basics,
Buttons,
ChoiceButtons,
Commander USING [CommandProc, Register],
Containers,
IconRegistry,
Icons,
IndexNotify,
IndexProperties,
IndexToolPrivate,
IndexTree,
IndexViewer,
IO,
Labels,
List,
Menus,
MessageWindow,
OrderedSymbolTableRef,
Rope,
Rules,
TEditDocument,
TEditScrolling,
TextNode,
TiogaOps,
TiogaOpsDefs,
TIPUser,
ViewerClasses,
ViewerOps,
ViewerSpecs,
ViewerTools;
IndexViewerImpl: CEDAR PROGRAM
IMPORTS Buttons, ChoiceButtons, Commander, Containers, IconRegistry, Icons, IndexNotify, IndexProperties, IndexToolPrivate, IndexTree, IO, Labels, List, Menus, MessageWindow, OrderedSymbolTableRef, Rope, Rules, TEditScrolling, TextNode, TiogaOps, TIPUser, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS IndexViewer, TiogaOpsDefs
= BEGIN OPEN IndexToolPrivate;
ROPE: TYPE = Rope.ROPE;
Comparison: TYPE = Basics.Comparison;
NodeBody: PUBLIC TYPE = TextNode.Body;
PhraseData: TYPE = REF PhraseDataRec;
PhraseDataRec: TYPE = RECORD [
indexHandle: IndexHandle,
phraseNumber: CARDINAL ← 0,
viewer: ViewerClasses.Viewer ← NIL];
NewTool: PUBLIC PROCEDURE [documentName: ROPE] RETURNS [indexHandle: IndexHandle ← NEW[IndexHandleRec]] = {
firstColumn: INTEGER = 0;
secondColumn: INTEGER = ViewerSpecs.openRightWidth/2;
columnWidth: INTEGER = ViewerSpecs.openRightWidth/2;
numberOfPhrases: INTEGER = 10;
phraseSize: INTEGER = secondColumn - 100;
defaultPhraseRows: INTEGER = 3;
h: INTEGER = ViewerSpecs.captionHeight;
rowH: INTEGER = ViewerSpecs.messageWindowHeight;
thisY: INTEGER ← 0;
NextRow: PROCEDURE [rows: INTEGER ← 1] = {
thisY ← thisY + rows*rowH;
};
menu: Menus.Menu ← Menus.CreateMenu[2];
container: ViewerClasses.Viewer ←
Containers.Create[
info: [
name: IF documentName.Length = 0 THEN "IndexTool" ELSE Rope.Concat["Index for ", documentName],
column: right,
iconic: TRUE,
scrollable: FALSE],
paint: TRUE];
InsertMenuEntry: PROCEDURE [name: Rope.ROPE, proc: Menus.MenuProc, line: Menus.MenuLine] = {
menuEntry: Menus.MenuEntry ← Menus.CreateEntry[
name: name,
proc: proc,
clientData: indexHandle,
documentation: NIL,
fork: FALSE,
guarded: FALSE];
Menus.InsertMenuEntry[menu, menuEntry, line];
};
PhraseButtons: PROCEDURE [label: ROPE, x, y: INTEGER] RETURNS [phraseContainer: ViewerClasses.Viewer] = {
saveY: INTEGER ← y;
Leave space for the header button above the phrase container
thisY ← y;
NextRow[];
phraseContainer ← Containers.Create[
info: [
scrollable: TRUE,
border: FALSE,
parent: container,
wx: x,
wy: thisY,
ww: columnWidth,
wh: defaultPhraseRows*rowH],
paint: FALSE];
FOR i: INTEGER DECREASING IN [1..numberOfPhrases] DO
Create the phrase viewers in reverse order since they are searched in list order by CollectPhrases
phraseData: PhraseData ← NEW[PhraseDataRec ← [indexHandle, i]];
button: Buttons.Button ← Buttons.Create[
info: [
name: " ",
wx: 0,
wy: (i-1)*rowH,
ww: 0,
wh: h,
parent: phraseContainer,
scrollable: FALSE,
border: TRUE],
clientData: phraseData,
proc: PhraseButton,
paint: FALSE];
phraseData.viewer ← ViewerTools.MakeNewTextViewer[
info: [
wx: button.cw + 6*i,
wy: (i-1)*rowH,
ww: phraseContainer.cw - (button.cw + 6*i) - 5,
wh: rowH,
parent: phraseContainer,
scrollable: TRUE,
border: TRUE],
paint: FALSE];
ENDLOOP;
[] ← Buttons.Create[
info: [
name: label,
wx: x,
wy: y,
wh: rowH,
parent: container,
scrollable: FALSE,
border: FALSE],
proc: ClearPhrasesButton,
clientData: phraseContainer,
paint: FALSE];
thisY ← y;
};
FeedbackLabel: PROCEDURE [label: ROPE, x, y: INTEGER] RETURNS[v: ViewerClasses.Viewer] = {
v ← Labels.Create[info: [
parent: container,
name: label,
wx: x,
wy: y,
wh: rowH,
border: FALSE]];
v ← ViewerTools.MakeNewTextViewer[info: [
parent: container,
wx: x + v.ww,
wy: y,
ww: columnWidth - v.ww - 40,
wh: rowH,
border: FALSE]];
ViewerTools.InhibitUserEdits[v];
};
Rule: PROCEDURE [h: CARDINAL] = {
rule: Rules.Rule ← Rules.Create[info: [
parent: container,
wx: 0,
wy: thisY,
ww: 0,
wh: h]];
Containers.ChildXBound[container, rule];
};
Choices: PROCEDURE [choices: LIST OF ROPE, x, y: INTEGER] RETURNS [choiceRef: ChoiceButtons.EnumTypeRef] = {
choiceRef ← ChoiceButtons.BuildEnumTypeSelection[viewer: container, x: x, y: y, buttonNames: choices, clientdata: indexHandle, style: menuSelection];
};
InsertMenuEntry[name: "CopyIndexTo", proc: CopyIndexToButton, line: 0];
InsertMenuEntry[name: "NewIndexTool", proc: NewIndexToolButton, line: 0];
InsertMenuEntry[name: "DeleteEntry", proc: DeleteEntryButton, line: 0];
InsertMenuEntry[name: "InsertEntry", proc: InsertEntryButton, line: 0];
InsertMenuEntry[name: "PermutePhrases", proc: PermutePhrasesButton, line: 1];
InsertMenuEntry[name: "SetPosition", proc: SetPositionButton, line: 1];
ViewerOps.SetMenu[container, menu];
indexHandle.indexPhrasesContainer ← PhraseButtons["Index Entry Phrases:", firstColumn, thisY];
indexHandle.sortAsPhrasesContainer ← PhraseButtons["Sort As:", secondColumn, thisY];
NextRow[4];
indexHandle.rangeChoice ← Choices[CONS["Single", CONS["BeginRange", CONS["EndRange", NIL]]], firstColumn, thisY];
indexHandle.primaryChoice ← Choices[CONS["Ordinary", CONS["Primary", NIL]], secondColumn, thisY];
NextRow[];
indexHandle.bytePositionBox ← FeedbackLabel["Byte position:", secondColumn, thisY];
indexHandle.seePhrasesContainer ← PhraseButtons["See phrases:", firstColumn, thisY];
NextRow[4];
Rule[1];
indexHandle.entriesBox ← FeedbackLabel["Entries:", firstColumn, thisY];
indexHandle.rangesBox ← FeedbackLabel["Ranges:", secondColumn, thisY];
NextRow[];
indexHandle.seeCountBox ← FeedbackLabel["See references:", firstColumn, thisY];
indexHandle.nestingCountBox ← FeedbackLabel["Deepest Nesting:", secondColumn, thisY];
NextRow[];
Rule[1];
indexHandle.indexViewer ← ViewerOps.CreateViewer[
flavor: $Index,
info: [
wx: firstColumn,
wy: thisY,
parent: container,
scrollable: TRUE],
paint: FALSE
];
indexHandle.indexViewer.tipTable ← indexViewerClass.tipTable;
Containers.ChildXBound[container, indexHandle.indexViewer];
Containers.ChildYBound[container, indexHandle.indexViewer];
ViewerOps.AddProp[indexHandle.indexViewer, $IndexHandle, indexHandle];
indexHandle.rootIndexBranch ← NARROW[indexHandle.indexViewer.data, TEditDocument.TEditDocumentData].text;
};
NewIndexToolButton: Menus.ClickProc = {
indexHandle: IndexHandle ← NARROW[clientData, IndexHandle];
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
documentName: ROPE
IF selectedViewer = NIL OR selectedViewer.class.get = NIL THEN NIL
ELSE NARROW[selectedViewer.class.get[selectedViewer, $SelChars]];
IF documentName.Length <= 1 THEN {
IF (selectedViewer = NIL) THEN {
Sorry["No selection in a text viewer"];
documentName ← NIL;
}
ELSE documentName ← selectedViewer.name;
};
[] ← IndexToolPrivate.CreateIndexFromDocument[documentName];
};
Sorry: PROCEDURE [rope: ROPE] = {
MessageWindow.Append[message: rope, clearFirst: TRUE];
MessageWindow.Blink[];
};
InsertEntryButton: Menus.ClickProc = {
indexHandle: IndexHandle ← NARROW[clientData, IndexHandle];
indexEntry: IndexEntry ← NEW[IndexEntryRec];
phrases: LIST OF ROPE ← NIL;
choice: ROPE;
ixRope: ROPE;
IF indexHandle.bytePosition = 0 THEN {
Sorry["No position established for this index entry yet"];
RETURN;
};
indexEntry.phrases ← CollectPhrases[indexHandle.indexPhrasesContainer];
indexEntry.bytePosition ← indexHandle.bytePosition;
indexEntry.stickyPointer ← TextNode.LocWithin[
TiogaOps.ViewerDoc[indexHandle.documentViewer],
indexHandle.bytePosition];
indexEntry.valid ← TRUE;
indexEntry.versionStamp ← IndexToolPrivate.IndexVersionStamp[TiogaOps.GetRope[indexEntry.stickyPointer.node]];
indexEntry.primary ← Rope.Equal["Primary", ChoiceButtons.GetSelectedButton[indexHandle.primaryChoice]];
choice ← ChoiceButtons.GetSelectedButton[indexHandle.rangeChoice];
indexEntry.type ← SELECT TRUE FROM
Rope.Equal["Single", choice] => single,
Rope.Equal["BeginRange", choice] => beginRange,
Rope.Equal["EndRange", choice] => endRange,
ENDCASE => ERROR;
indexEntry.seePhrases ← CollectPhrases[indexHandle.seePhrasesContainer];
IF indexEntry.seePhrases # NIL THEN indexEntry.type ← see;
indexEntry.sortAsPhrases ← CollectPhrases[indexHandle.sortAsPhrasesContainer];
IndexTree.InsertNewIndexEntry[indexHandle, indexEntry ! IndexTree.DuplicateKey => GOTO Duplicate];
ixRope ← IndexProperties.IndexEntryToRope[indexEntry];
ixRope ← Rope.Concat[NARROW[TiogaOps.GetProp[indexEntry.stickyPointer.node, $Index], ROPE], ixRope];
TiogaOps.PutProp[indexEntry.stickyPointer.node, $Index, ixRope];
ViewerOps.SetNewVersion[indexHandle.documentViewer];
set feedback selection and normalize indexViewer
NormalizeIndexViewer[indexHandle, indexEntry];
EXITS
Duplicate => Sorry["Index entry duplicates one already in table; NOT inserted."];
};
NormalizeIndexViewer: PROCEDURE [indexHandle: IndexHandle, ix: IndexEntry] = {
SetFeedback[indexHandle, ix];
ViewerOps.PaintViewer[indexHandle.indexViewer, client];
};
SetFeedback: PROCEDURE [indexHandle: IndexHandle, ix: IndexEntry] = {
nearestEntry: IndexEntry;
item, leftItem, equalItem, rightItem: OrderedSymbolTableRef.Item;
[leftItem, equalItem, rightItem] ← OrderedSymbolTableRef.Lookup3[indexHandle.indexTable, ix];
item ← IF equalItem # NIL THEN equalItem ELSE IF leftItem # NIL THEN leftItem ELSE rightItem;
nearestEntry ← NARROW[item, IndexEntry];
TiogaOps.SelectNodes[viewer: indexHandle.indexViewer,
start: nearestEntry.branchLocationStart.node,
end: nearestEntry.branchLocationEnd.node,
level: word,
which: feedback];
TEditScrolling.AutoScroll[indexHandle.indexViewer, TRUE, FALSE, feedback];
};
CollectPhrases: PROCEDURE [phraseContainer: ViewerClasses.Viewer] RETURNS [phrases: LIST OF ROPENIL] = {
v: ViewerClasses.Viewer ← phraseContainer.child;
phrase: ROPE;
WHILE v # NIL DO
IF v.scrollable THEN {
phrase ← ViewerTools.GetContents[v];
IF phrase = NIL OR phrase.Length = 0 OR phrase.Equal[" "] THEN EXIT;
TRUSTED {phrases ← LOOPHOLE[List.Nconc1[LOOPHOLE[phrases], phrase]]};
};
v ← v.sibling;
ENDLOOP;
};
DeleteEntryButton: Menus.ClickProc = {
indexHandle: IndexHandle ← NARROW[clientData, IndexHandle];
};
PermutePhrasesButton: Menus.ClickProc = {
indexHandle: IndexHandle ← NARROW[clientData, IndexHandle];
};
CopyIndexToButton: Menus.ClickProc = {
indexHandle: IndexHandle ← NARROW[clientData, IndexHandle];
IF TiogaOps.GetSelection[primary].viewer = NIL THEN {
Sorry["No primary selection. Can't copy index."];
RETURN;
};
TiogaOps.SelectNodes[viewer: indexHandle.indexViewer,
start: TiogaOps.FirstChild[indexHandle.rootIndexBranch],
end: TiogaOps.LastLocWithin[indexHandle.rootIndexBranch].node,
which: secondary];
TiogaOps.ToPrimary[];
};
SetPositionButton: Menus.ClickProc = {
indexHandle: IndexHandle ← NARROW[clientData, IndexHandle];
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
should check, but we don't yet, that selectedViewer is the documentViewer!
IF selectedViewer # NIL THEN {
indexHandle.bytePosition ← ViewerTools.GetSelection[selectedViewer].start;
ViewerTools.SetContents[indexHandle.bytePositionBox, IO.PutFR["%g", IO.card[indexHandle.bytePosition]]];
};
};
ClearPhrasesButton: Buttons.ButtonProc = {
phraseContainer: ViewerClasses.Viewer ← NARROW[clientData, ViewerClasses.Viewer];
v: ViewerClasses.Viewer ← phraseContainer.child;
WHILE v # NIL DO
ViewerTools.SetContents[v, NIL];
v ← v.sibling;
ENDLOOP;
};
PhraseButton: Menus.ClickProc = {
phraseData: PhraseData ← NARROW[clientData, PhraseData];
indexHandle: IndexHandle ← phraseData.indexHandle;
atTheBeginning: ViewerTools.SelPos = NEW[ViewerTools.SelPosRec ← [0, 0, FALSE, before]];
SELECT mouseButton FROM
yellow =>
Sorry["Left-click to copy selection to phrase viewer, Right-click to clear phrase viewer"];
red => {
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
phrase: ROPE
IF selectedViewer = NIL OR selectedViewer.class.get = NIL THEN NIL
ELSE NARROW[selectedViewer.class.get[selectedViewer, $SelChars]];
IF phrase.Length < 1 THEN {
Sorry["No selection in a text viewer"];
phrase ← NIL;
}
ELSE {
IF (indexHandle.bytePosition = 0 OR phraseData.phraseNumber = 1) AND selectedViewer # NIL THEN {
indexHandle.bytePosition ← ViewerTools.GetSelection[selectedViewer].start;
ViewerTools.SetContents[indexHandle.bytePositionBox, IO.PutFR["%g", IO.card[indexHandle.bytePosition]]];
};
ViewerTools.SetContents[phraseData.viewer, phrase];
ViewerTools.SetSelection[phraseData.viewer, atTheBeginning];
};
};
blue => {
ViewerTools.SetContents[phraseData.viewer, NIL];
ViewerTools.SetSelection[phraseData.viewer, atTheBeginning];
};
ENDCASE;
};
ExecCommand: Commander.CommandProc = {
documentName: ROPENIL;
documentName ← IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc !
IO.EndOfStream => CONTINUE].token;
[] ← IndexToolPrivate.CreateIndexFromDocument[documentName];
};
IndexViewerPaint: ViewerClasses.PaintProc = {
IF self.iconic THEN {
Icons.DrawIcon[flavor: indexViewerIcon,
context: context, x: 0, y: 0,
label: self.name];
}
ELSE
textPaintProc[self, context, whatChanged, clear];
};
indexViewerClass: ViewerClasses.ViewerClass =
NEW[ViewerClasses.ViewerClassRec ← ViewerOps.FetchViewerClass[$Text]^];
textPaintProc: ViewerClasses.PaintProc = indexViewerClass.paint;
indexViewerIcon: Icons.IconFlavor;
indexViewerClass.notify ← IndexNotify.IndexNotifier;
indexViewerClass.cursor ← bullseye;
indexViewerClass.paint ← IndexViewerPaint;
indexViewerClass.icon ← private;
indexViewerClass.tipTable ← TIPUser.InstantiateNewTIPTable["IndexTool.tip"];
IconRegistry.RegisterIcon["IndexTool", "IndexTool.icons", 0];
indexViewerIcon ← IconRegistry.GetIcon["IndexTool", tool];
ViewerOps.RegisterViewerClass[$Index, indexViewerClass];
Commander.Register[
key: "IndexTool",
proc: ExecCommand,
doc: "Create an index tool."
];
END.