<> <> <> <> <<>> DIRECTORY Ascii USING [CR, LF], Atom USING [GetPropFromList, PropList, PutPropOnList], Char USING [XCHAR, Widen], Commander USING [CommandProc, Register], CommanderOps USING [ArgumentVector, Failed, Parse], Imager, NodeStyle USING [GetInt, Style], NodeStyleOps USING [Alloc, ApplyAll, Free, OfStyle], PFS USING [Error, PathFromRope], TiogaIO USING [FromFile], Real USING [Round], Rope USING [Concat, ROPE], RopeReader, Scaled USING [FromInt, Round, Value], TEdit, TEditFormat USING [Allocate, FormatLine, LineInfo, Paint, Release], TextEdit, TextNode, Tioga, Xl, XTk, XTkBitmapWidgets, XTkContainers, XTkFriends, XTkScroller, XTkTioga, XTkWidgets; XTkTiogaImpl: CEDAR PROGRAM IMPORTS Atom, Char, Commander, CommanderOps, Imager, NodeStyle, NodeStyleOps, PFS, Real, Rope, RopeReader, Scaled, TiogaIO, TEditFormat, TextEdit, TextNode, Xl, XTk, XTkBitmapWidgets, XTkContainers, XTkFriends, XTkScroller, XTkWidgets EXPORTS XTkTioga ~ BEGIN OPEN TEdit; ROPE: TYPE ~ Rope.ROPE; Node: TYPE ~ Tioga.Node; Doc: TYPE ~ TEdit.Doc; View: TYPE ~ TEdit.View; <> CreateDoc: PROC [fileName: ROPE] RETURNS [Doc] ~ { root: Node ~ TiogaIO.FromFile[PFS.PathFromRope[fileName]].root; doc: Doc ~ NEW[TEdit.DocRep ¬ [root: root]]; FOR id: TEdit.SelectionId IN TEdit.SelectionId DO doc.selection[id] ¬ NEW[TEdit.SelectionRep ¬ []]; ENDLOOP; RETURN[doc]; }; CreateView: PROC [doc: Doc] RETURNS [View] ~ { view: View ~ NEW[TEdit.ViewRep ¬ [doc: doc]]; doc.views ¬ CONS[view, doc.views]; view.scrollLoc ¬ [TextNode.FirstChild[doc.root], 0]; RETURN[view]; }; commentTest: ARRAY TEdit.CommentFilter OF ARRAY BOOL--node.comment-- OF BOOL ~ [ includeComments: [FALSE: TRUE, TRUE: TRUE], excludeComments: [FALSE: TRUE, TRUE: FALSE], onlyComments: [FALSE: FALSE, TRUE: TRUE] ]; Level: PROC [node: Node, level: INTEGER ¬ 0] RETURNS [INTEGER] ~ --INLINE?-- { RETURN[IF level>0 THEN level ELSE TextNode.Level[node]]; }; NodeIsViewable: PROC [view: View, node: Node, level: INTEGER ¬ 0] RETURNS [BOOL] ~ { RETURN[commentTest[view.commentFilter][node.comment] AND Level[node, level]<=view.clipLevel]; }; FirstViewableNode: PROC [view: View, start: Node, startLevel: INTEGER ¬ 0] RETURNS [node: Node, level: INTEGER] ~ { level ¬ Level[(node ¬ start), startLevel]; UNTIL node=NIL OR NodeIsViewable[view, node, level] DO [node, level] ¬ TextNode.ForwardClipped[node: node, maxLevel: view.clipLevel, nodeLevel: level]; ENDLOOP; }; FirstPosInView: PROC [view: View, start: TextNode.Location, startLevel: INTEGER ¬ 0] RETURNS [node: Node ¬ NIL, where: INT ¬ 0, level: INTEGER ¬ 0] ~ { level ¬ Level[(node ¬ start.node), startLevel]; WHILE level>view.clipLevel DO node ¬ TextNode.Parent[node]; level ¬ level-1; ENDLOOP; [node, level] ¬ FirstViewableNode[view, node, level]; where ¬ IF node=start.node THEN start.where ELSE 0; IF view.firstLinesOnly THEN where ¬ 0; }; NextNodeInView: PROC [view: View, start: Node, startLevel: INTEGER ¬ 0] RETURNS [node: Node, level: INTEGER] ~ { [node, level] ¬ TextNode.ForwardClipped[node: start, maxLevel: view.clipLevel, nodeLevel: startLevel]; RETURN FirstViewableNode[view, node, level]; }; PrevNodeInView: PROC [view: View, start: Node, startParent: Node ¬ NIL, startLevel: INTEGER ¬ 0 ] RETURNS [node: Node, parent: Node, level: INTEGER] ~ { [node, parent, level] ¬ TextNode.BackwardClipped[node: start, maxLevel: view.clipLevel, parent: startParent, nodeLevel: startLevel]; UNTIL parent=NIL OR NodeIsViewable[view, node, level] DO [node, parent, level] ¬ TextNode.BackwardClipped[node: node, maxLevel: view.clipLevel, parent: parent, nodeLevel: level]; ENDLOOP; }; NextPosInView: PROC [view: View, line: TEdit.Line, lineLevel: INTEGER ¬ 0] RETURNS [node: Node ¬ NIL, where: INT ¬ 0, level: INTEGER ¬ 0] ~ { level ¬ Level[(node ¬ line.info.startPos.node), lineLevel]; IF line.info.break=eon OR view.firstLinesOnly THEN [node, level] ¬ NextNodeInView[view, node, level] ELSE where ¬ line.info.startPos.where+line.info.nChars; }; GetLine: PROC [view: View, index: NAT] RETURNS [TEdit.Line] ~ { size: NAT ~ IF view.lines=NIL THEN 0 ELSE view.lines.size; IF NOT index> topLeading: INT ~ NodeStyle.GetInt[GetStyle[node], topLeading]; leading ¬ MAX[bottomLeadingPrev, topLeading]; } ELSE leading ¬ NodeStyle.GetInt[GetStyle[node], leading]; -- other lines of node }; baseline ¬ baselinePrev+leading; IF prev#NIL THEN { -- if not the first line, test for off bottom <> yTest: INT ~ IF node.hasArtwork THEN bottomPrev ELSE baseline; IF yTest >= view.ch THEN EXIT; -- off bottom }; line ¬ GetLine[view, nLines]; TEditFormat.FormatLine[lineInfo: line.info, node: node, startOffset: where, nodeStyle: GetStyle[node], lineWidth: lineWidth]; minLineGap ¬ NodeStyle.GetInt[GetStyle[node], minLineGap]; IF (bottomPrev+minLineGap)>(baseline-line.info.ymax) THEN { baseline ¬ bottomPrev+minLineGap+line.info.ymax; IF (NOT node.hasArtwork) AND (baseline >= view.ch) THEN EXIT; -- now off bottom }; line.baseline ¬ baseline; nLines ¬ nLines+1; -- add the line IF level>maxLevel THEN maxLevel ¬ level; [node, where, level] ¬ NextPosInView[view, line, level]; ENDLOOP; view.nLines ¬ nLines; NodeStyleOps.Free[cachedStyle]; view.stopLoc ¬ [node, where]; view.maxLevel ¬ maxLevel; ComputeLineResolveValues[view]; SetScroll[view.window, (view.range ¬ ComputeVisibleRange[view])]; IF paint THEN PaintView[view, TRUE]; }; ComputeVisibleRange: PROC [view: View] RETURNS [VisibleRange] ~ { IF view.nLines>0 THEN { ENABLE TextNode.BadArgs => CONTINUE; root: Node ~ view.doc.root; stop: TextNode.Location ~ view.stopLoc; pos0: TextNode.Location ~ [TextNode.FirstChild[root], 0]; -- first loc in doc pos3: TextNode.Location ~ TextNode.LastLocWithin[root]; -- last loc in doc pos1: TextNode.Location ~ view.lines[0].info.startPos; -- first loc in view pos2: TextNode.Location ~ IF stop.node#NIL THEN stop ELSE pos3; -- loc after view c1: INT ~ TextNode.LocOffset[pos0, pos1]; -- to top of view c2: INT ~ c1+TextNode.LocOffset[pos1, pos2]; -- to bottom of view c3: INT ~ c2+TextNode.LocOffset[pos2, pos3]; -- to end of document divisor: REAL ~ c3; RETURN[[c1/divisor, c2/divisor]]; }; RETURN[[0, 1]]; }; ComputeLineResolveValues: PROC [view: View] ~ { prev: TEdit.Line ¬ NIL; FOR i: NAT IN[0..view.nLines) DO line: TEdit.Line ~ view.lines[i]; top: INTEGER ~ line.baseline-line.info.ymax; bot: INTEGER ~ line.baseline-line.info.ymin; line.resolve ¬ bot; IF prev#NIL THEN { mid: INTEGER ~ (prev.resolve+top)/2; prev.resolve ¬ MAX[prev.baseline, MIN[mid, line.baseline]]; }; prev ¬ line; ENDLOOP; }; PaintView: PROC [view: View, clear: BOOL ¬ FALSE] ~ { action: PROC [context: Imager.Context] ~ { IF clear THEN { Imager.SetGray[context, 0]; Imager.MaskRectangleI[context, 0, 0, view.cw, view.ch]; Imager.SetGray[context, 1]; }; FOR i: NAT IN[0..view.nLines) DO line: TEdit.Line ~ view.lines[i]; Imager.SetXYI[context, Scaled.Round[line.info.xOffset], view.ch-line.baseline]; TEditFormat.Paint[line.info, context]; IF view.firstLinesOnly AND line.info.break#eon THEN Imager.ShowRope[context, "..."]; ENDLOOP; }; Paint[view.window, action]; }; ResolveToLine: PROC [view: View, y: INTEGER] RETURNS [line: Line ¬ NIL, belowLine: BOOL ¬ FALSE] ~ { FOR i: NAT IN[0..view.nLines) DO line ¬ view.lines[i]; IF y> <> <> <> <> <backStop DO>> <