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; 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 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]; 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 ROPE _ NIL] = { 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 = { }; 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[]; 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: ROPE _ NIL; 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.  IndexViewerImpl.mesa Copyright c 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 Leave space for the header button above the phrase container Create the phrase viewers in reverse order since they are searched in list order by CollectPhrases set feedback selection and normalize indexViewer indexHandle: IndexHandle _ NARROW[clientData, IndexHandle]; should check, but we don't yet, that selectedViewer is the documentViewer! Κ’˜codešœ™Kšœ Οmœ1™K˜—K˜K˜K˜—š œ˜&Kšœžœ˜;KšœG˜GK™Jšžœžœžœ˜KšœJ˜JKšœh˜hKšœ˜—K˜K˜—š œ˜*Kšœ(žœ#˜QKšœ0˜0šžœžœž˜Kšœžœ˜ K˜Kšž˜—K˜K˜—š  œ˜!Kšœžœ˜8Kšœ2˜2Kšœ%žœ žœ ˜Xšžœ ž˜šœ ˜ Kšœ[˜[—šœ˜KšœG˜Gšœžœ˜Kš žœžœžœžœžœž˜BKšžœžœ6˜A—šžœžœ˜Kšœ'˜'Kšœ ž˜ K˜—šžœ˜š žœžœžœžœžœ˜`KšœJ˜JKšœh˜hKšœ˜—Kšœ3˜3Kšœ<˜