//License Agreement: // The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); // you may not use this file except in compliance with the License. You may obtain a copy of the License at // http://www.skyhunter.com/marcs/Ebrowser-license.html // Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF // ANY KIND, either express or implied. See the License for the specific language governing rights and // limitations under the License. //The Original Code is The Ebrowser Program. //The Initial Developer of the Original Code is Marc Stiegler. Portions created by // Marc Stiegler are Copyright (C) Marc Stiegler. All Rights Reserved. //********** # set up tracing; stub out all the printing for operational version define traceln(str) { println(str) } traceln("started") define versionNumber := "0.1" def makeErr(errorDescription) :any {import:java.lang.Exception new(errorDescription)} define io(className) :any {(import:java.io.* )[className]} //String Line Reader // Note: getLineNumber gives you the number of the line you are about to read, not // the number of the line you just read // Also note, readLine() returns null at end-of-stream def stringLineReaderMaker new(theString) : any { def stringReader := import:java.io.StringReader new(theString) def lineReader := import:java.io.LineNumberReader new(stringReader) def stringLineReader { to iterate(func) { def next := lineReader readLine while (next != null) { func(lineReader getLineNumber - 1,next) next := lineReader readLine } } delegate {lineReader} } } //uiSet define uiSet { to labelPad() :any {8} to fontMetrics(component) :any {component getFontMetrics(component getFont)} to swing(className) :any { (import:javax.swing.* )[className] } to awt(className) :any { (import:java.awt.* )[className] } to baseWidth(component) { def goodHeight := uiSet fontMetrics(component) getHeight + uiSet labelPad() component.preferredSize := uiSet awt("Dimension") new (150,goodHeight) component.maximumSize := uiSet awt("Dimension") new(2000,goodHeight) component.alignmentX := 0.5 } to varyWidth(comp) { uiSet baseWidth(comp) def goodWidth := (uiSet fontMetrics(comp) stringWidth(comp getLabel)) + uiSet labelPad() def goodHeight := uiSet fontMetrics(comp) getHeight + uiSet labelPad() comp.preferredSize := uiSet awt("Dimension") new (goodWidth, goodHeight) comp.maximumSize := uiSet awt("Dimension") new (2000, goodHeight) } to fixWidth (comp) { uiSet baseWidth(comp) def widthPad := (uiSet fontMetrics(comp) stringWidth(" ")) def goodWidth := (uiSet fontMetrics(comp) stringWidth(comp getLabel toString)) + widthPad traceln("rigid width" +goodWidth + " on " + comp.label toString) def goodHeight := uiSet fontMetrics(comp) getHeight + uiSet labelPad() comp.maximumSize := uiSet awt("Dimension") new (goodWidth,goodHeight) comp.preferredSize := uiSet awt("Dimension") new (goodWidth,goodHeight) } to attachAction(component,target,verb) { define listener { to actionPerformed(event) { E call(target, verb, []) } } component addActionListener(listener) } to newButton(labelText, verb, target) :any { define button := E call(uiSet swing("JButton"), "new(String)", [labelText]) uiSet varyWidth(button) button.background := (uiSet awt("SystemColor") control); uiSet attachAction(button,target,verb) button } to addComponents(container, componentList) { for each in componentList { E call(container, "add(Component)", [each]); } } to addMenuItem(menu,labelText, verb, target) { define item := E call(uiSet swing("JMenuItem"), "new(String)", [labelText]) uiSet attachAction(item, target, verb) E call(menu, "add(JMenuItem)", [item]) } } // eDialog: make textFieldDefaultValue null to not show a textfield // If window closed with close box, not button, getClickedButton and // getEnteredText both return null // returns a promise for a dialog that has been answered define eDialogPromiser new(title, label, textFieldDefaultValue, buttonNames) :any { define myClickedButton := null define [finishedDialogPromise, resolver] := PromiseMaker() def eDialog := { def myWin := uiSet swing("JFrame") new(title) define windowListener { to windowClosing(event) { resolver resolve(eDialog) } to windowClosed(event) { myWin dispose } match _ {} } myWin addWindowListener(windowListener) traceln("added window listener") def mainPane := myWin.contentPane mainPane.preferredSize := uiSet awt("Dimension") new (400,200) def labelPane := E call(uiSet swing("JTextField"), "new(String)",["\n" +label]) labelPane setEditable(false) labelPane.background := (uiSet awt("SystemColor") control); def labelScrollPane := uiSet swing("JScrollPane") new(labelPane) mainPane add(labelScrollPane, "North") def myTextField := null if (textFieldDefaultValue != null) { myTextField := uiSet swing("JTextField") new(textFieldDefaultValue) uiSet baseWidth(myTextField) mainPane add(myTextField, "Center") } def buttonRow := uiSet swing("JPanel") new() buttonRow setLayout(uiSet awt("GridLayout") new(1,buttonNames size)) mainPane add(buttonRow, "South") traceln("about to build dialog buttons") for index in 0..(buttonNames size - 1) { def nextButton := uiSet newButton(buttonNames[index], "button" + index, eDialog) uiSet addComponents(buttonRow,[nextButton]) } traceln("made buttons") def clickedButton(index) { myClickedButton := buttonNames[index] resolver resolve(eDialog) myWin dispose } myWin pack myWin show def eDialog { to getEnteredText() : any { def answer := null if (myClickedButton != null && textFieldDefaultValue != null) { answer := myTextField.text} answer } to getClickedButton() : any {myClickedButton} to button0 { clickedButton(0)} to button1 { clickedButton(1)} to button2 { clickedButton(2)} to button3 { clickedButton(3)} } } finishedDialogPromise } //finisher def promiseAllDone(thePromiseList) :any { //resolves after all in list resolved, returns true if all promises fulfilled def [resolutionPromise,resolver] := PromiseMaker() def promiseCount := thePromiseList size define breakMessages := "" if (promiseCount == 0) {resolver resolve(true)} for each in thePromiseList { each <- whenResolved(def obs(result) { if (E isBroken(result)) {breakMessages := breakMessages + " :" +result} promiseCount -= 1 if (promiseCount == 0) { if (breakMessages size == 0) { resolver resolve(true) } else { resolver smash(makeErr (breakMessages)) } } }) } resolutionPromise } def vowsMonitorMaker new() :any { def [bundlePromise,resolver] := PromiseMaker() def bundle := [] diverge def vowsMonitor { to add (thePromise) {bundle push(thePromise)} to finishAll {resolver resolve(promiseAllDone(bundle))} to promiseFinish :any {bundlePromise} } } //close whole app when windowset returns to zero size define windowSet := [] asKeys diverge define standardWindow (title, windowClosingHandler) :any { define mainFrame := uiSet swing("JFrame") new(title) define mainPane := mainFrame.contentPane define border := uiSet swing("BoxLayout") new(mainPane,1) mainPane.layout := border; windowSet[mainFrame] := mainFrame define windowListener { to windowClosing(event) { windowClosingHandler windowClosing() } to windowClosed(event) { windowSet removeKey(mainFrame) if (windowSet size == 0) { interp continueAtTop } } match _ {} } mainFrame addWindowListener(windowListener) mainFrame } def popAboutBox() { def text := "For Help with Ebrowser, see http://www.skyhunter.com/marcs/eBrowserIndex.html Copyright 1999 Marc Stiegler. All rights reserved. This software covered by the Mozilla license. For details see http://www.skyhunter.com/marcs/Ebrowser-license.html" eDialogPromiser new("About Ebrowser", text, null, ["OK"]) } //Indentation formatter define prettyE(eText,tab) : any { define outputStream := E call(import:java.lang.StringBuffer , "new(int)",[eText size + 10]) define flowIndentLevel := 0 define continuationIndent := "" for line in (stringLineReaderMaker new(eText)) { define strippedLine := line strippedLine := strippedLine trim if(strippedLine size == 0) { E call(outputStream, "append(String)",["\n"]) continuationIndent := "" } else { if (strippedLine startsWith("}")) { flowIndentLevel := (flowIndentLevel - 1) max(0) } E call(outputStream, "append(String)", [continuationIndent + (tab * flowIndentLevel) + strippedLine + "\n"]) //outputStream print(continuationIndent) //outputStream print(tab * flowIndentLevel) //outputStream println(strippedLine) continuationIndent := "" if (strippedLine endsWith("\\")) { continuationIndent := tab } if (strippedLine endsWith ("{")) { flowIndentLevel += 1 } } } outputStream toString } //Ebrowser define EbrowserMaker new(browseFile) :any { //def tabValue := " " def tabValue := "\t" define Ebrowser := { def myText := browseFile getText() def funcLineIndices := [] def myFileName := browseFile getName() def closingInProcess := false define mainFrame := standardWindow("",Ebrowser) define mainPane := mainFrame.contentPane mainFrame setDefaultCloseOperation(uiSet swing("WindowConstants") DO_NOTHING_ON_CLOSE) define border := uiSet awt("BorderLayout") new() mainPane.layout := border def saveButton := uiSet newButton ("Save","save",Ebrowser) def findButton := uiSet newButton("Find","find",Ebrowser) def searchField := uiSet swing("JTextField") new() def refreshFuncButton := uiSet newButton("Refresh","refreshFuncs",Ebrowser) def aboutButton := uiSet newButton("About","about",Ebrowser) def gotoButton := uiSet newButton("Goto Line", "goto",Ebrowser) def reformatButton := uiSet newButton("Reformat","reformat",Ebrowser) def buttonPane := uiSet swing("JPanel") new() buttonPane setLayout(uiSet awt("GridLayout") new(2,1)) def topButtonPane := uiSet swing("JPanel") new() topButtonPane setLayout(uiSet awt("GridLayout") new(1,4)) def bottomButtonPane := uiSet swing("JPanel") new() bottomButtonPane setLayout(uiSet awt("GridLayout") new(1,3)) bottomButtonPane add(findButton, 0) bottomButtonPane add(searchField,1) bottomButtonPane add(gotoButton,2) topButtonPane add(saveButton, 0) topButtonPane add(refreshFuncButton,1) topButtonPane add(reformatButton,2) topButtonPane add(aboutButton,3) buttonPane add(topButtonPane,0) buttonPane add(bottomButtonPane,1) def textPane := uiSet swing("JTextArea") new() textPane.lineWrap := false textPane.tabSize := 4 textPane setFont(uiSet awt("Font") new("Monospaced",uiSet awt("Font") PLAIN,12)) def scrollingTextPane := uiSet swing("JScrollPane") new(uiSet swing("ScrollPaneConstants") VERTICAL_SCROLLBAR_ALWAYS, uiSet swing("ScrollPaneConstants") HORIZONTAL_SCROLLBAR_ALWAYS) uiSet addComponents(scrollingTextPane getViewport, [textPane]) def funcListPane := E call(uiSet swing("JList"),"new(Vector)",[import:java.util.Vector new]) funcListPane setFont(uiSet awt("Font") new("Monospaced",uiSet awt("Font") PLAIN,12)) def scrollingFuncPane := uiSet swing("JScrollPane") new(uiSet swing("ScrollPaneConstants") VERTICAL_SCROLLBAR_ALWAYS, uiSet swing("ScrollPaneConstants") HORIZONTAL_SCROLLBAR_ALWAYS) uiSet addComponents(scrollingFuncPane getViewport, [funcListPane]) def funcListPaneListener { to mouseClicked(theEvent) { if (theEvent getClickCount() > 1) { def selectedLineNum := funcLineIndices[funcListPane getSelectedIndex] def newPosition := textPane getLineStartOffset(selectedLineNum) textPane setCaretPosition(textPane getLineStartOffset(selectedLineNum + 1) - 1) textPane moveCaretPosition(newPosition) } } match _ { } } funcListPane addMouseListener(funcListPaneListener) scrollingFuncPane setMinimumSize(uiSet awt("Dimension") new (150, 100)) scrollingTextPane setMinimumSize(uiSet awt("Dimension") new (300, 100)) def splitter := uiSet swing("JSplitPane") new splitter setLeftComponent(scrollingFuncPane) splitter setRightComponent(scrollingTextPane) def statusPane := E call(uiSet swing("JLabel"), "new(String)",[" "]) statusPane setFont(uiSet awt("Font") new("Monospaced",uiSet awt("Font") PLAIN,11)) mainPane add(buttonPane,"North") mainPane add(splitter, "Center") mainPane add(statusPane, "South") mainFrame setTitle("Ebrowser-- " + myFileName) textPane setText(myText) textPane setCaretPosition(0) mainFrame pack() mainFrame setSize(700,500) mainFrame show() define Ebrowser { to save { myText := textPane getText browseFile setText(myText) textPane requestFocus() } to setStatus(statusText) { statusPane setText(statusText) statusPane repaint } to find { Ebrowser setStatus("Finding...") def textSize := textPane getText size def startSearchIndex := textPane getCaretPosition def searchArea := textPane getText(startSearchIndex, textSize - startSearchIndex) if (searchArea =~ `@{front}${searchField getText}@{tail}`) { def startFoundIndex := front size + startSearchIndex textPane setCaretPosition(startFoundIndex ) textPane moveCaretPosition(startFoundIndex + searchField getText size) Ebrowser setStatus("Found") textPane requestFocus() } else {Ebrowser setStatus("Not Found")} } to goto { def lineNum := (import:java.lang.Integer new(searchField getText)) intValue def newPosition := textPane getLineStartOffset(lineNum) textPane setCaretPosition(textPane getLineStartOffset(lineNum + 1) - 1) textPane moveCaretPosition(newPosition) textPane requestFocus() } to about {popAboutBox()} to reformat { textPane setEditable(false) Ebrowser setStatus("Reformatting...") textPane setText(prettyE(textPane getText, tabValue)) textPane setEditable(true) Ebrowser setStatus("Reformat Done.") } to refreshFuncs { Ebrowser setStatus("Refreshing Outline...") def spaces := " " def computeIndent(leadString) : any { def spaceCount := 0 def indentCount := 0 for each in leadString { if (each == '\t') { indentCount += 1 } else { spaceCount += 1 } } indentCount := indentCount + (spaceCount _/ 4) spaces substring(0,indentCount+ 1) } def newList := import:java.util.Vector new //E call(funcListPane, "setListData(Vector)", [newList]); funcLineIndices := [] diverge def lines := stringLineReaderMaker new(textPane getText) for eachIndex => each in lines { if (each =~ `@{leader}def@{defineSuffix} @{name}${"{"}@{theEnd}`) { newList addElement(computeIndent(leader) + "def " + name) funcLineIndices push(eachIndex) } else if (each =~ `@{leader}to @{name}${"{"}@{theEnd}`) { newList addElement(computeIndent(leader) + "to " + name) funcLineIndices push(eachIndex) } } E call(funcListPane, "setListData(Vector)", [newList]); Ebrowser setStatus("Outline refreshed") } to windowClosing() { if (!closingInProcess) { closingInProcess := true def finalText:= textPane getText if (finalText == myText) { mainFrame dispose() } else { def saveDecision := eDialogPromiser new("Modified File " + myFileName, "Save Before Exiting?", null, ["Save", "Exit", "Cancel"]) (saveDecision <- getClickedButton) <- whenResolved (def o(answer) { closingInProcess := false if (answer == null) { answer := "Cancel" } if (answer =="Save") { traceln("into save") Ebrowser save mainFrame dispose } else if (answer == "Exit") { myText := finalText mainFrame dispose } }) } } } } } } traceln("about to make controller") def fileName := "newEfile.e" def commandArgs := interp getArgs if (commandArgs size > 0) { fileName := commandArgs[0] } define baseBrowser := EbrowserMaker new(file: fileName) interp blockAtTop