DIRECTORY Basics, Buttons, ChoiceButtons, Commander USING [CommandProc, Register], Containers, IconRegistry, Icons, IndexNotify, IndexProperties, IndexToolPrivate, IndexTree, IndexViewer, IO, Labels, List, Menus, MessageWindow, NumberLabels USING [CreateNumber, NumberLabelUpdate], 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, NumberLabels, 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 [kindOfIndex: ATOM, 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]; ViewerOps.AddProp[phraseData.viewer, $IndexPhrase, $TRUE]; 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, charsInNumber: NAT _ 4] RETURNS[v: NumberLabels.NumberLabel] = { v _ Labels.Create[ info: [name: label, parent: container, wx: x, wy: y, wh: rowH, border: FALSE] paint: FALSE ]; v _ NumberLabels.CreateNumber[ info: [ parent: container, wx: x + v.ww, wy: y, ww: columnWidth - v.ww - 40, wh: rowH, border: FALSE], chars: charsInNumber, initialValue: 0, paint: FALSE ]; }; 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: Phrases, 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: "AddNewKind", proc: AddNewKindButton, line: 1]; InsertMenuEntry[name: "PermutePhrases", proc: PermutePhrasesButton, line: 1]; ViewerOps.SetMenu[container, menu]; indexHandle.kindOfEntryChoices _ Choices[LIST["Ordinary", "See", $SeeAlso], firstColumn, thisY]; NextRow[]; indexHandle.indexPhrasesContainer _ PhraseButtons["Index Entry Phrases:", firstColumn, thisY]; indexHandle.sortAsPhrasesContainer _ PhraseButtons["Sort As:", secondColumn, thisY]; NextRow[4]; indexHandle.seePhrasesContainer _ PhraseButtons["See phrases:", firstColumn, thisY]; NextRow[4]; Rule[1]; indexHandle.entriesLabel _ FeedbackLabel["# Entries:", firstColumn, thisY]; indexHandle.seeCountLabel _ FeedbackLabel["# See references:", secondColumn, thisY]; NextRow[]; indexHandle.startPositionLabel _ FeedbackLabel["Start position:", firstColumn, thisY]; indexHandle.endPositionLabel _ FeedbackLabel["End position:", secondColumn, thisY]; NextRow[]; Rule[1]; 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 = { selectedViewer: ViewerClasses.Viewer _ ViewerTools.GetSelectedViewer[]; documentName: ROPE _ ViewerTools.GetSelectionContents[]; IF selectedViewer = NIL THEN { Sorry["Make a text selection of the document name (longer than 1 character)"]; documentName _ NIL; } ELSE IF documentName.Length <= 1 THEN 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]; selectedViewer: ViewerClasses.Viewer _ ViewerTools.GetSelectedViewer[]; IF selectedViewer # NIL THEN { Sorry["Make a text selection in the document for this index entry]; RETURN; } ELSE IF selectedViewer # indexHandle.documentViewer THEN { Sorry["Can't index that document from this index tool"]; RETURN; } ELSE { phrases: Phrases _ CollectPhrases[indexHandle.indexPhrasesContainer]; IF NumberOfPhrases[phrases] = 0 THEN { Sorry["Supply an index phrase before inserting it!]; RETURN; } ELSE { ix: IndexEntry _ NEW[IndexEntryRec]; ix.phrases _ phrases; ix.kindOfEntry _ ChoiceButtons.GetSelectedButton[indexHandle.kindOfEntryChoices]; ix.seePhrases _ CollectPhrases[indexHandle.seePhrasesContainer]; IF ix.seePhrases # NIL AND ix.kindOfEntry # $See AND ix.kindOfEntry # $SeeAlso THEN ix.kindOfEntry _ $See; ix.sortAsPhrases _ CollectPhrases[indexHandle.sortAsPhrasesContainer]; IndexTree.InsertNewIndexEntry[indexHandle, ix ! IndexTree.DuplicateKey => GOTO Duplicate]; ViewerOps.SetNewVersion[indexHandle.documentViewer]; NormalizeIndexViewer[indexHandle, ix]; UpdateFeedback[indexHandle.entriesLabel, indexHandle.entries _ indexHandle.entries + 1]; IF indexHandle.kindOfEntry = $See THEN UpdateFeedback[indexHandle.seeCountLabel, indexHandle.seeCount _ indexHandle.seeCount + 1]; UpdateFeedback[indexHandle.nestingCountLabel, indexHandle.nestingCount _ MAX[NumberOfPhrases[indexHandle.phrases], indexHandle.nestingCount]]; }; }; EXITS Duplicate => Sorry["Index entry duplicates one already in table; NOT inserted."]; }; NormalizeIndexViewer: 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]; ViewerOps.PaintViewer[indexHandle.indexViewer, client]; }; CollectPhrases: PROCEDURE [phraseContainer: ViewerClasses.Viewer] RETURNS [phrases: Phrases _ NIL] = { v: ViewerClasses.Viewer _ phraseContainer.child; phrase: ViewerTools.TiogaContents; WHILE v # NIL DO IF ViewerOps.FetchProp[v, $IndexPhrase] # NIL THEN { phrase _ ViewerTools.GetTiogaContents[v]; IF phrase = NIL OR phrase.contents.IsEmpty = 0 THEN EXIT; TRUSTED {phrases _ LOOPHOLE[List.Nconc1[LOOPHOLE[phrases], phrase.contents]]}; TRUSTED {phrases _ LOOPHOLE[List.Nconc1[LOOPHOLE[phrases], phrase.formatting]]}; }; v _ v.sibling; ENDLOOP; }; NumberOfPhrases: PROC [phrases: Phrases] RETURNS [NAT] ~ TRUSTED { RETURN [List.Length[LOOPHOLE[phrases]]/2]; }; DeleteEntryButton: Menus.ClickProc = { indexHandle: IndexHandle _ NARROW[clientData, IndexHandle]; }; PermutePhrasesButton: Menus.ClickProc = { }; AddNewKindButton: Menus.ClickProc = { indexHandle: IndexHandle _ NARROW[clientData, IndexHandle]; ix.kindOfEntry _ ChoiceButtons.GetSelectedButton[indexHandle.kindOfEntryChoices]; }; 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[]; }; UpdateFeedback: PROC [numberLabel: NumberLabels.NumberLabels, value: INT] ~ { IF numberLabel.destroyed THEN RETURN; NumberLabels.NumberLabelUpdate[numberLabel, value]; }; 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; ViewerTools.SetSelection[phraseContainer.child, atTheBeginning]; }; 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[]; IF selectedViewer = NIL THEN Sorry["Please make a text selection of the phrase to index"]; ELSE { start, end: TiogaOps.Location; root: TiogaOps.Ref _ TiogaOps.SelectionRoot[primary] [start~start, end~end] _ TiogaOps.GetSelection[primary]; IF start.node # end.node THEN Sorry["Index phrases must be within a single node"]; ELSE { ViewerTools.SetContents[phraseData.viewer, NIL]; TiogaOps.SelectDocument[phraseData.viewer, char, secondary]; TiogaOps.ToSecondary[]; -- copy the selected phrase }; }; }; blue => { ViewerTools.SetContents[phraseData.viewer, NIL]; ViewerTools.SetSelection[phraseData.viewer, atTheBeginning]; }; ENDCASE; }; IndexToolCommand: Commander.CommandProc = { documentName: ROPE _ NIL; documentName _ IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc ! IO.EndOfStream => CONTINUE].token; [] _ IndexToolPrivate.CreateIndexFromDocument[$Index, 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: IndexToolCommand, 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, March 31, 1985 2:11:35 am 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 indexHandle.indexViewer _ ViewerOps.CreateViewer[ flavor: $TiogaButtons, info: [ wx: firstColumn, wy: thisY, parent: container, scrollable: TRUE], paint: FALSE ]; check that the selection is in the expected document indexHandle.documentViewer establish position from current selection span: TiogaOps.GetSelection[primary].start & .end IF indexHandle.bytePosition = 0 THEN { Sorry["No position established for this index entry yet"]; RETURN; }; ixRope _ IndexProperties.IndexEntryToRope[ix]; ixRope _ Rope.Concat[NARROW[TiogaOps.GetProp[ix.stickyPointer.node, $Index], ROPE], ixRope]; TiogaOps.PutProp[ix.stickyPointer.node, $Index, ixRope]; TextEdit.PutCharProp[node~, index~, name~ix.kindOfIndex, value~ix, nChars~, root~RootOf[node]]; set feedback selection and normalize indexViewer indexHandle: IndexHandle _ NARROW[clientData, IndexHandle]; check that the phrase is within a single node we should accept a document kind and create an atom with that name ΚΔ˜codešœ™Kšœ Οmœ1™K˜—K˜K˜K˜—š œžœ1žœ˜MJšžœžœžœ˜%Jšœ3˜3J˜J˜—š œ˜*Kšœ(žœ#˜QKšœ0˜0šžœžœž˜Kšœžœ˜ K˜Kšž˜—Kšœ@˜@K˜K˜—š  œ˜!Kšœžœ˜8Kšœ2˜2Kšœ%žœ žœ ˜Xšžœ ž˜šœ ˜ Kšœ[˜[—šœ˜KšœG˜Gšžœžœž˜Kšœ=˜=—šžœ˜K˜Kšœ4˜4K˜8K™-šžœž˜J˜4—šžœ˜Kšœ+žœ˜0Kšœ<˜