(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* Last modified on Mon Dec 15 10:43:46 PST 1997 by heydon *) (* modified on Tue Feb 7 11:43:41 PST 1995 by gnelson *) <* PRAGMA LL *> <* PRAGMA SPEC *> MODULE ToolBox; IMPORT View, Drawing, Editor, Source, CurrCmd, JunoConfig; IMPORT JunoAST, JunoASTUtils; IMPORT VBT, TextVBT, Filter, PaintOp, Split, BorderedVBT; IMPORT ButtonVBT, MenuBtnVBT, AnchorBtnVBT; IMPORT Atom; REVEAL Button = ButtonPublic BRANDED "ToolBox.Button" OBJECT root: View.Root; tool: Drawing.ArgTool; OVERRIDES init := ButtonInit; END; REVEAL SetButton = SetButtonPublic BRANDED "ToolBox.SetButton" OBJECT root: View.Root; tool: Drawing.SetTool; OVERRIDES init := SetButtonInit END; TYPE PointButton = Button BRANDED "ToolBox.PointButton" OBJECT END; TextButton = Button BRANDED "ToolBox.TextButton" OBJECT END; SetAnchorBtn = AnchorBtnVBT.T BRANDED "ToolBox.SetAnchorBtn" OBJECT ed: Editor.T; nm: Atom.T; OVERRIDES pre := SetAnchorBtnPre END; TemplButton = Button BRANDED "TemplButton" OBJECT cmd: JunoAST.Cmd END; (* These are the types for the three kinds of buttons appearing in a toolbox. The first two are normal buttons for point tools and text tools, and the third is an anchor for a set tool menu. That menu will be a VSplit of "SetButton"'s. If "btn: SetAnchorBtn", then "btn.ed" is the editor in which the procedure for this button appears, and "nm" is the name of the button's procedure.*) VAR (* CONST *) toolColor := ARRAY BOOLEAN OF PaintOp.ColorQuad{ PaintOp.bgFg, PaintOp.MakeColorQuad(PaintOp.Fg, PaintOp.Bg)}; PROCEDURE ButtonText(name: TEXT): TextVBT.T = (* Return a "TextVBT.T" with the name "name" left-justified in the current tool font "JunoConfig.TextFont". *) BEGIN RETURN TextVBT.New(name, halign := 0.0, fnt := JunoConfig.textFont) END ButtonText; PROCEDURE ButtonInit( self: Button; root: View.Root; name: TEXT; tl: Drawing.ArgTool): Button = (* This is the implementation of the "Button.init" method. *) BEGIN self.root := root; self.tool := tl; tl.label := name; EVAL ButtonVBT.T.init(self, ButtonText(name), DoButton); RETURN self END ButtonInit; PROCEDURE DoButton(self: ButtonVBT.T; READONLY cd: VBT.MouseRec) = (* This is the call-back procedure for a "Button". It selects the tool associated with the button. If the tool has any arguments, this procedure also permanently highlights the button (and unhighlights the button of the current tool if one was selected). *) <* LL.sup = VBT.mu *> VAR tb: Button := self; BEGIN TYPECASE self OF TemplButton (b) => CurrCmd.ChangeAST(b.root.ccmd, b.cmd); b.root.astTrue := TRUE; b.root.source.modified(View.ModKind.ImplicitConsistent); b.root.ccmd.codeValid := FALSE; b.root.dTrue := FALSE; IF NOT Source.Make(b.root.source, cd.time, b.root.skipify) THEN b.root.astTrue := FALSE; EVAL Source.Make(b.root.source, cd.time, b.root.skipify) END; RETURN ELSE (* SKIP *) END; Drawing.SelectTool(tb.root.drawing, tb.tool, cd.time); IF NUMBER(tb.tool.argType^) # 0 THEN HiliCurrButton(tb.root, hili := FALSE); tb.root.currButton := tb; HiliCurrButton(tb.root, hili := TRUE) END END DoButton; PROCEDURE SetButtonInit( self: SetButton; root: View.Root; name: TEXT; tl: Drawing.SetTool): SetButton = (* This is the implementation of the "SetButton.init" method. *) BEGIN self.root := root; self.tool := tl; tl.label := name; EVAL MenuBtnVBT.T.init(self, ButtonText(name), DoSetButton); RETURN self END SetButtonInit; PROCEDURE DoSetButton(self: ButtonVBT.T; READONLY cd: VBT.MouseRec) = (* This is the callback procedure for a "SetButton". It selects the tool associated with the button when it was created. *) <* LL.sup = VBT.mu *> VAR tb: SetButton := self; BEGIN Drawing.SelectTool(tb.root.drawing, tb.tool, cd.time) END DoSetButton; PROCEDURE SetAnchorBtnPre(btn: SetAnchorBtn) = (* This is the implementation of the "pre" method for the "AnchorBtnVBT.T" of a set tool. It dynamically updates its menu from the menu stored in the button's editor "btn.ed" under the name "btn.nm". *) <* LL.sup = VBT.mu *> VAR menu := btn.ed.getMenu(btn.nm); BEGIN IF btn.menu = NIL THEN btn.menu := BorderedVBT.New(menu) ELSIF menu # Filter.Child(btn.menu) THEN EVAL Filter.Replace(btn.menu, menu) END; AnchorBtnVBT.T.pre(btn) END SetAnchorBtnPre; PROCEDURE Update(t: T; ed: Editor.T; rt: View.Root; n:=0; anon:=TRUE) = <* LL.sup = VBT.mu *> <* FATAL Split.NotAChild *> VAR ch: ButtonVBT.T := Split.Nth(t, n); mod := JunoAST.NilId; tr := Editor.Trees(ed); BEGIN (* The buttons in the toolbox preceeding "ch" correspond to the first "n" buttons plus the buttons for the trees preceeding "tr". *) WHILE tr # NIL DO TYPECASE tr.tree OF JunoAST.Module(m) => IF NOT anon THEN mod := m.name END | JunoAST.PredDecl (p) => IF NOT p.private THEN VAR hd := p.header; type: Atom.T; BEGIN IF ed.getToolType(hd.name, type) THEN <* ASSERT type = Editor.PointToolSym *> VAR nmMatch: BOOLEAN; BEGIN IF PredMatch(mod, ch, hd, nmMatch) THEN ch := Split.Succ(t, ch) ELSE InsertBtn(t, ch, NewPredBtn(rt, mod, hd), nmMatch) END END END END END | JunoAST.FuncDecl (f) => IF NOT f.private THEN VAR hd := f.header; type: Atom.T; BEGIN IF ed.getToolType(hd.name, type) THEN <* ASSERT type = Editor.PointToolSym *> VAR nmMatch: BOOLEAN; BEGIN IF FuncMatch(mod, ch, hd, nmMatch) THEN ch := Split.Succ(t, ch) ELSE InsertBtn(t, ch, NewFuncBtn(rt, mod, hd), nmMatch) END END END END END | JunoAST.ProcDecl (p) => IF NOT p.private THEN VAR hd := p.header; type: Atom.T; BEGIN IF ed.getToolType(hd.name, type) THEN VAR nmMatch: BOOLEAN; BEGIN IF type # Editor.TemplToolSym AND ProcMatch(mod, ch, hd, nmMatch) AND BtnTypeMatches(ch, type) THEN ch := Split.Succ(t, ch) ELSE InsertBtn(t, ch, NewProcBtn(rt, ed, mod, p, type), nmMatch) END END END END END ELSE (* SKIP *) END; tr := tr.next END; WHILE ch # NIL DO VAR nextCh := Split.Succ(t, ch); BEGIN Split.Delete(t, ch); ch := nextCh END END END Update; PROCEDURE PredMatch(mod: JunoAST.Id; btn: ButtonVBT.T; sig: JunoAST.PredHeader; VAR (*OUT*) nameMatch: BOOLEAN): BOOLEAN = (* Returns "TRUE" iff both the "name" and "signature" of "btn" and "sig" match. If "FALSE" is returned, then "nameMatch" is set "TRUE" iff the "name" of "btn" matches that of "sig". Otherwise, the value of "nameMatch" is undefined. *) BEGIN TYPECASE btn OF NULL => (* SKIP *) | PointButton (b) => TYPECASE b.tool OF Drawing.PredTool (tool) => nameMatch := (tool.name.id0 = mod AND tool.name.id1 = sig.name); RETURN nameMatch AND (tool.in_cnt = sig.ins.size) | Drawing.ArgTool (tool) => nameMatch := (tool.name.id0 = mod AND tool.name.id1 = sig.name); RETURN FALSE END ELSE (* SKIP *) END; nameMatch := FALSE; RETURN FALSE END PredMatch; PROCEDURE FuncMatch(mod: JunoAST.Id; btn: ButtonVBT.T; sig: JunoAST.FuncHeader; VAR (*OUT*) nameMatch: BOOLEAN): BOOLEAN = (* Returns "TRUE" iff both the "name" and "signature" of "btn" and "sig" match. If "FALSE" is returned, then "nameMatch" is set "TRUE" iff the "name" of "btn" matches that of "sig". Otherwise, the value of "nameMatch" is undefined. *) BEGIN TYPECASE btn OF NULL => (* SKIP *) | PointButton (b) => TYPECASE b.tool OF Drawing.FuncTool (tool) => nameMatch := (tool.name.id0 = mod AND tool.name.id1 = sig.name); RETURN nameMatch AND (tool.in_cnt = sig.ins.size) | Drawing.ArgTool (tool) => nameMatch := (tool.name.id0 = mod AND tool.name.id1 = sig.name); RETURN FALSE END ELSE (* SKIP *) END; nameMatch := FALSE; RETURN FALSE END FuncMatch; PROCEDURE ProcMatch(mod: JunoAST.Id; btn: ButtonVBT.T; sig: JunoAST.ProcHeader; VAR (*OUT*) nameMatch: BOOLEAN): BOOLEAN = (* Returns "TRUE" iff both the "name" and "signature" of "btn" and "sig" match. If "FALSE" is returned, then "nameMatch" is set "TRUE" iff the "name" of "btn" matches that of "sig". Otherwise, the value of "nameMatch" is undefined. In the case that "btn" is a "SetAnchorBtn", which has no signature, the signatures of "btn" and "sig" are always considered to match. Otherwise, the signatures match if the signature of the tool associated with "btn" matches that of "sig". *) BEGIN TYPECASE btn OF <* NOWARN *> NULL => (* SKIP *) | SetAnchorBtn (b) => nameMatch := FALSE; RETURN b.nm = sig.name | Button (b) => TYPECASE b.tool OF Drawing.ProcTool (tool) => nameMatch := (tool.name.id0 = mod AND tool.name.id1 = sig.name); RETURN nameMatch AND (tool.in_cnt = sig.ins.size AND tool.out_cnt = sig.outs.size AND tool.inout_cnt=sig.inouts.size) | Drawing.ArgTool (tool) => nameMatch := (tool.name.id0 = mod AND tool.name.id1 = sig.name); RETURN FALSE END END; nameMatch := FALSE; RETURN FALSE END ProcMatch; PROCEDURE BtnTypeMatches(btn: ButtonVBT.T; type: Atom.T): BOOLEAN = (* Return "TRUE" iff the type of "btn" matches the UI declaration type name "type", which is one of "Editor.PointToolSym", "Editor.TextToolSym", or "Editor.SetToolSym". *) BEGIN TYPECASE btn OF <* NOWARN *> PointButton => RETURN type = Editor.PointToolSym | TemplButton => RETURN type = Editor.TemplToolSym | TextButton => RETURN type = Editor.TextToolSym | SetAnchorBtn => RETURN type = Editor.SetToolSym END END BtnTypeMatches; PROCEDURE InsertBtn(t: T; VAR (*INOUT*) ch: ButtonVBT.T; new: ButtonVBT.T; nameMatch: BOOLEAN) = (* If "nameMatch", then replace "ch" by "new" in "t", and set "ch" to the successor child of this new child. In this case, "ch" must be non-NIL. Otherwise, insert "new" before "ch" in "t", and leave "ch" unchanged. *) <* FATAL Split.NotAChild *> BEGIN IF nameMatch THEN <* ASSERT ch # NIL *> VAR curr := ch; BEGIN ch := Split.Succ(t, ch); Split.Delete(t, curr) END END; Split.Insert(t, Split.Pred(t, ch), new) END InsertBtn; PROCEDURE NewPredBtn( rt: View.Root; mod: JunoAST.Id; hd: JunoAST.PredHeader): Button = BEGIN RETURN NEW(PointButton).init(rt, Atom.ToText(hd.name), Drawing.NewPredTool(JunoASTUtils.QIdFromIds(mod, hd.name), hd.ins.size)) END NewPredBtn; PROCEDURE NewFuncBtn( rt: View.Root; mod: JunoAST.Id; hd: JunoAST.FuncHeader): Button = BEGIN RETURN NEW(PointButton).init(rt, Atom.ToText(hd.name), Drawing.NewFuncTool(JunoASTUtils.QIdFromIds(mod, hd.name), hd.ins.size)) END NewFuncBtn; PROCEDURE NewProcBtn( rt: View.Root; ed: Editor.T; mod: Atom.T; p: JunoAST.ProcDecl; type: Atom.T): ButtonVBT.T = VAR hd := p.header; txtName := Atom.ToText(hd.name); BEGIN IF type = Editor.SetToolSym THEN RETURN NEW(SetAnchorBtn, ed := ed, nm := hd.name).init( ButtonText(txtName), menu := NIL, hfudge := 10.0, n := 999) ELSE VAR res: Button; BEGIN IF type = Editor.TextToolSym THEN res := NEW(TextButton) ELSIF type = Editor.PointToolSym THEN res := NEW(PointButton) ELSIF type = Editor.TemplToolSym THEN res := NEW(TemplButton, cmd := StripIFProj(p.body)) ELSE <* ASSERT FALSE *> END; RETURN res.init(rt, txtName, Drawing.NewProcTool(JunoASTUtils.QIdFromIds(mod, hd.name), hd.ins.size, hd.outs.size, hd.inouts.size, isText := type = Editor.TextToolSym)) END END END NewProcBtn; PROCEDURE StripIFProj(body: JunoAST.Cmd): JunoAST.Cmd = BEGIN TYPECASE body OF JunoAST.If (if) => IF ISTYPE(if.body, JunoAST.Proj) THEN RETURN if.body ELSE RETURN body END ELSE RETURN body END END StripIFProj; <* SPEC Unselect REQUIRES sup(LL) = VBT.mu *> PROCEDURE Unselect(rt: View.Root) = BEGIN IF rt.drawing # NIL THEN Drawing.SelectTool(rt.drawing, NIL, time := 0) END; HiliCurrButton(rt, hili := FALSE); rt.currButton := NIL END Unselect; PROCEDURE HiliCurrButton(rt: View.Root; hili: BOOLEAN) = (* If the current button associated with "rt" is non-NIL, hilight or unhilight it as "hili" is "TRUE" OR "FALSE". *) BEGIN IF rt.currButton # NIL THEN TextVBT.SetFont(Filter.Child(rt.currButton), JunoConfig.textFont, toolColor[hili]) END END HiliCurrButton; PROCEDURE SwapButton(t: T; curr, new: Drawing.ArgTool; newLabel: TEXT) = <* LL.sup = VBT.mu *> <* FATAL Split.NotAChild *> VAR ch: Button := Split.Succ(t, NIL); BEGIN WHILE ch # NIL AND ch.tool # curr DO ch := Split.Succ(t, ch) END; <* ASSERT ch # NIL *> Split.Replace(t, ch, NEW(Button).init(ch.root, newLabel, new)) END SwapButton; BEGIN END ToolBox.