|
Editor |
|
package ij.plugin.frame; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.awt.datatransfer.*; import ij.*; import ij.gui.*; import ij.util.Tools; import ij.text.TextWindow; import ij.macro.*; import ij.plugin.MacroInstaller; import ij.plugin.NewPlugin; import ij.io.SaveDialog; /** This is a simple TextArea based editor for editing and compiling plugins. */ public class Editor extends PlugInFrame implements ActionListener, ItemListener, TextListener, ClipboardOwner, MacroConstants { public static String JavaScriptIncludes = "importPackage(Packages.ij);"+ "importPackage(Packages.ij.gui);"+ "importPackage(Packages.ij.process);"+ "importPackage(Packages.ij.measure);"+ "importPackage(java.lang);"+ "importPackage(java.awt);"+ "function print(s) {IJ.log(s);};"; public static String JS_NOT_FOUND = "JavaScript.jar was not found in the plugins\nfolder. It can be downloaded from:\n \n"+IJ.URL+"/download/tools/JavaScript.jar"; public static final int MAX_SIZE=28000, XINC=10, YINC=18; public static final int MONOSPACED=1, MENU_BAR=2; public static final int MACROS_MENU_ITEMS = 6; static final String FONT_SIZE = "editor.font.size"; static final String FONT_MONO= "editor.font.mono"; private TextArea ta; private String path; private boolean changes; private static String searchString = ""; private static int lineNumber = 1; private static int xoffset, yoffset; private static int nWindows; private Menu fileMenu, editMenu; private Properties p = new Properties(); private int[] macroStarts; private String[] macroNames; private MenuBar mb; private Menu macrosMenu; private int nMacros; private Program pgm; private int eventCount; private String shortcutsInUse; private int inUseCount; private MacroInstaller installer; private static String defaultDir; private boolean dontShowWindow; private int[] sizes = {9, 10, 11, 12, 13, 14, 16, 18, 20, 24, 36, 48, 60, 72}; private int fontSize = (int)Prefs.get(FONT_SIZE, 5); private CheckboxMenuItem monospaced; private static boolean caseSensitive = true; private static boolean wholeWords; private boolean isMacroWindow; public Editor() { this(16, 60, 0, MENU_BAR); } public Editor(int rows, int columns, int fontSize, int options) { super("Editor"); WindowManager.addWindow(this); addMenuBar(options); ta = new TextArea(rows, columns); ta.addTextListener(this); if (IJ.isLinux()) ta.setBackground(Color.white); addKeyListener(IJ.getInstance()); // ImageJ handles keyboard shortcuts add(ta); pack(); if (fontSize<0) fontSize = 0; if (fontSize>=sizes.length) fontSize = sizes.length-1; setFont(); positionWindow(); //display("Test.java", ""); IJ.register(Editor.class); } void addMenuBar(int options) { mb = new MenuBar(); if (Menus.getFontSize()!=0) ; mb.setFont(Menus.getFont()); Menu m = new Menu("File"); m.add(new MenuItem("New...", new MenuShortcut(KeyEvent.VK_N, true))); m.add(new MenuItem("Open...", new MenuShortcut(KeyEvent.VK_O))); m.add(new MenuItem("Save", new MenuShortcut(KeyEvent.VK_S))); m.add(new MenuItem("Save As...")); m.add(new MenuItem("Print...", new MenuShortcut(KeyEvent.VK_P))); m.addActionListener(this); fileMenu = m; mb.add(m); m = new Menu("Edit"); String key = IJ.isMacintosh()?" Cmd ":" Ctrl+"; MenuItem item = new MenuItem("Undo"+key+"Z"); item.setEnabled(false); m.add(item); m.addSeparator(); boolean shortcutsBroken = IJ.isWindows() && (System.getProperty("java.version").indexOf("1.1.8")>=0 ||System.getProperty("java.version").indexOf("1.5.")>=0); if (shortcutsBroken) item = new MenuItem("Cut Ctrl+X"); else item = new MenuItem("Cut",new MenuShortcut(KeyEvent.VK_X)); m.add(item); if (shortcutsBroken) item = new MenuItem("Copy Ctrl+C"); else item = new MenuItem("Copy", new MenuShortcut(KeyEvent.VK_C)); m.add(item); if (shortcutsBroken) item = new MenuItem("Paste Ctrl+V"); else item = new MenuItem("Paste",new MenuShortcut(KeyEvent.VK_V)); m.add(item); m.addSeparator(); m.add(new MenuItem("Find...", new MenuShortcut(KeyEvent.VK_F))); m.add(new MenuItem("Find Next", new MenuShortcut(KeyEvent.VK_G))); m.add(new MenuItem("Go to Line...", new MenuShortcut(KeyEvent.VK_L))); m.addSeparator(); m.add(new MenuItem("Select All", new MenuShortcut(KeyEvent.VK_A))); m.add(new MenuItem("Zap Gremlins")); m.addActionListener(this); mb.add(m); editMenu = m; if ((options&MENU_BAR)!=0) setMenuBar(mb); m = new Menu("Font"); m.add(new MenuItem("Make Text Smaller", new MenuShortcut(KeyEvent.VK_N))); m.add(new MenuItem("Make Text Larger", new MenuShortcut(KeyEvent.VK_M))); m.addSeparator(); monospaced = new CheckboxMenuItem("Monospaced Font", Prefs.get(FONT_MONO, false)); if ((options&MONOSPACED)!=0) monospaced.setState(true); monospaced.addItemListener(this); m.add(monospaced); m.add(new MenuItem("Save Settings")); m.addActionListener(this); mb.add(m); } public void positionWindow() { Dimension screen = IJ.getScreenSize(); Dimension window = getSize(); if (window.width==0) return; int left = screen.width/2-window.width/2; int top = (screen.height-window.height)/4; if (top<0) top = 0; if (nWindows<=0 || xoffset>8*XINC) {xoffset=0; yoffset=0;} setLocation(left+xoffset, top+yoffset); xoffset+=XINC; yoffset+=YINC; nWindows++; } void setWindowTitle(String title) { Menus.updateWindowMenuItem(getTitle(), title); setTitle(title); } public void create(String name, String text) { ta.append(text); if (IJ.isMacOSX()) IJ.wait(25); // needed to get setCaretPosition() on OS X ta.setCaretPosition(0); setWindowTitle(name); boolean macroExtension = name.endsWith(".txt") || name.endsWith(".ijm"); if (macroExtension || name.endsWith(".js")|| name.indexOf(".")==-1) { macrosMenu = new Menu("Macros"); macrosMenu.add(new MenuItem("Run Macro", new MenuShortcut(KeyEvent.VK_R))); macrosMenu.add(new MenuItem("Evaluate Line", new MenuShortcut(KeyEvent.VK_E))); macrosMenu.add(new MenuItem("Abort Macro")); macrosMenu.add(new MenuItem("Install Macros", new MenuShortcut(KeyEvent.VK_I))); macrosMenu.add(new MenuItem("Function Finder...", new MenuShortcut(KeyEvent.VK_F, true))); macrosMenu.addSeparator(); macrosMenu.add(new MenuItem("Evaluate JavaScript", new MenuShortcut(KeyEvent.VK_J, false))); macrosMenu.addSeparator(); // MACROS_MENU_ITEMS must be updated if items are added to this menu macrosMenu.addActionListener(this); mb.add(macrosMenu); if (macroExtension && text.indexOf("macro ")!=-1) installMacros(text, false); } else { fileMenu.addSeparator(); fileMenu.add(new MenuItem("Compile and Run", new MenuShortcut(KeyEvent.VK_R))); } if (IJ.getInstance()!=null && !dontShowWindow) show(); if (dontShowWindow) { dispose(); dontShowWindow = false; } WindowManager.setWindow(this); changes = false; } public void createMacro(String name, String text) { create(name, text); editMenu.add(new MenuItem("Convert to Plugin")); } void installMacros(String text, boolean installInPluginsMenu) { String functions = Interpreter.getAdditionalFunctions(); if (functions!=null && text!=null) { if (!(text.endsWith("\n") || functions.startsWith("\n"))) text = text + "\n" + functions; else text = text + functions; } installer = new MacroInstaller(); installer.setFileName(getTitle()); int nShortcutsOrTools = installer.install(text, macrosMenu); if (installInPluginsMenu || nShortcutsOrTools>0) installer.install(null); dontShowWindow = installer.isAutoRunAndHide(); } public void open(String dir, String name) { path = dir+name; File file = new File(path); try { StringBuffer sb = new StringBuffer(5000); BufferedReader r = new BufferedReader(new FileReader(file)); while (true) { String s=r.readLine(); if (s==null) break; else sb.append(s+"\n"); } r.close(); create(name, new String(sb)); changes = false; } catch (Exception e) { IJ.error(e.getMessage()); return; } } public String getText() { if (ta==null) return ""; else return ta.getText(); } public TextArea getTextArea() { return ta; } public void display(String title, String text) { ta.selectAll(); ta.replaceRange(text, ta.getSelectionStart(), ta.getSelectionEnd()); ta.setCaretPosition(0); setWindowTitle(title); changes = false; if (IJ.getInstance()!=null) show(); WindowManager.setWindow(this); } void save() { if (path==null) { saveAs(); return; } File f = new File(path); if (f.exists() && !f.canWrite()) { IJ.showMessage("Editor", "Unable to save because file is write-protected. \n \n" + path); return; } String text = ta.getText(); char[] chars = new char[text.length()]; text.getChars(0, text.length(), chars, 0); try { BufferedReader br = new BufferedReader(new CharArrayReader(chars)); BufferedWriter bw = new BufferedWriter(new FileWriter(path)); while (true) { String s = br.readLine(); if (s==null) break; bw.write(s, 0, s.length()); bw.newLine(); } bw.close(); IJ.showStatus(text.length()+" chars saved to " + path); changes = false; } catch (IOException e) {} } void compileAndRun() { if (path==null) saveAs(); if (path!=null) { save(); IJ.runPlugIn("ij.plugin.Compiler", path); } } void runMacro() { if (getTitle().endsWith(".js")) {evaluateJavaScript(); return;} int start = ta.getSelectionStart(); int end = ta.getSelectionEnd(); String text; if (start==end) text = ta.getText(); else text = ta.getSelectedText(); new MacroRunner(text); } void evaluateJavaScript() { if (!getTitle().endsWith(".js")) setTitle(SaveDialog.setExtension(getTitle(), ".js")); int start = ta.getSelectionStart(); int end = ta.getSelectionEnd(); String text; if (start==end) text = ta.getText(); else text = ta.getSelectedText(); if (text.equals("")) return; if (IJ.isJava16() && !IJ.isMacOSX()) { IJ.runPlugIn("JavaScriptEvaluator", text); return; } else { Object js = IJ.runPlugIn("JavaScript", JavaScriptIncludes+text); if (js==null) IJ.error(JS_NOT_FOUND); } } void evaluateLine() { int start = ta.getSelectionStart(); int end = ta.getSelectionEnd(); if (end>start) {runMacro(); return;} String text = ta.getText(); while (start>0) { start--; if (text.charAt(start)=='\n') {start++; break;} } while (end<text.length()-1) { end++; if (text.charAt(end)=='\n') break; } ta.setSelectionStart(start); ta.setSelectionEnd(end); runMacro(); } void print () { PrintJob pjob = Toolkit.getDefaultToolkit().getPrintJob(this, "Cool Stuff", p); if (pjob != null) { Graphics pg = pjob.getGraphics( ); if (pg != null) { String s = ta.getText(); printString(pjob, pg, s); pg.dispose( ); } pjob.end( ); } } void printString (PrintJob pjob, Graphics pg, String s) { int pageNum = 1; int linesForThisPage = 0; int linesForThisJob = 0; int topMargin = 30; int leftMargin = 30; int bottomMargin = 30; if (!(pg instanceof PrintGraphics)) throw new IllegalArgumentException ("Graphics contextt not PrintGraphics"); if (IJ.isMacintosh()) { topMargin = 0; leftMargin = 0; bottomMargin = 0; } StringReader sr = new StringReader (s); LineNumberReader lnr = new LineNumberReader (sr); String nextLine; int pageHeight = pjob.getPageDimension().height - bottomMargin; Font helv = new Font(getFontName(), Font.PLAIN, 10); pg.setFont (helv); FontMetrics fm = pg.getFontMetrics(helv); int fontHeight = fm.getHeight(); int fontDescent = fm.getDescent(); int curHeight = topMargin; try { do { nextLine = lnr.readLine(); if (nextLine != null) { nextLine = detabLine(nextLine); if ((curHeight + fontHeight) > pageHeight) { // New Page pageNum++; linesForThisPage = 0; pg.dispose(); pg = pjob.getGraphics(); if (pg != null) pg.setFont (helv); curHeight = topMargin; } curHeight += fontHeight; if (pg != null) { pg.drawString (nextLine, leftMargin, curHeight - fontDescent); linesForThisPage++; linesForThisJob++; } } } while (nextLine != null); } catch (EOFException eof) { // Fine, ignore } catch (Throwable t) { // Anything else t.printStackTrace(); } } String detabLine(String s) { if (s.indexOf('\t')<0) return s; int tabSize = 4; StringBuffer sb = new StringBuffer((int)(s.length()*1.25)); char c; for (int i=0; i<s.length(); i++) { c = s.charAt(i); if (c=='\t') { for (int j=0; j<tabSize; j++) sb.append(' '); } else sb.append(c); } return sb.toString(); } boolean copy() { String s; s = ta.getSelectedText(); Clipboard clip = getToolkit().getSystemClipboard(); if (clip!=null) { StringSelection cont = new StringSelection(s); clip.setContents(cont,this); return true; } else return false; } void cut() { if (copy()) { int start = ta.getSelectionStart(); int end = ta.getSelectionEnd(); ta.replaceRange("", start, end); if (IJ.isMacOSX()) ta.setCaretPosition(start); } } void paste() { String s; s = ta.getSelectedText(); Clipboard clipboard = getToolkit( ). getSystemClipboard(); Transferable clipData = clipboard.getContents(s); try { s = (String)(clipData.getTransferData(DataFlavor.stringFlavor)); } catch (Exception e) { s = e.toString( ); } int start = ta.getSelectionStart( ); int end = ta.getSelectionEnd( ); ta.replaceRange(s, start, end); if (IJ.isMacOSX()) ta.setCaretPosition(start+s.length()); } public void actionPerformed(ActionEvent evt) { String what = evt.getActionCommand(); if ("Save".equals(what)) save(); else if ("Compile and Run".equals(what)) compileAndRun(); else if ("Run Macro".equals(what)) runMacro(); else if ("Evaluate Line".equals(what)) evaluateLine(); else if ("Abort Macro".equals(what)) { Interpreter.abort(); IJ.beep(); } else if ("Install Macros".equals(what)) installMacros(ta.getText(), true); else if ("Function Finder...".equals(what)) new FunctionFinder(); else if ("Evaluate JavaScript".equals(what)) evaluateJavaScript(); else if ("Print...".equals(what)) print(); else if (what.startsWith("Paste")) paste(); else if (what.startsWith("Copy")) copy(); else if (what.startsWith("Cut")) cut(); else if ("Save As...".equals(what)) saveAs(); else if ("Select All".equals(what)) selectAll(); else if ("Find...".equals(what)) find(null); else if ("Find Next".equals(what)) find(searchString); else if ("Go to Line...".equals(what)) gotoLine(); else if ("Zap Gremlins".equals(what)) zapGremlins(); else if ("Convert to Plugin".equals(what)) convertToPlugin(); else if ("Make Text Larger".equals(what)) changeFontSize(true); else if ("Make Text Smaller".equals(what)) changeFontSize(false); else if ("Save Settings".equals(what)) saveSettings(); else if ("New...".equals(what)) IJ.run("Text Window"); else if ("Open...".equals(what)) IJ.open(); else installer.runMacro(what); } public void textValueChanged(TextEvent evt) { if (isMacroWindow) return; // first few textValueChanged events may be bogus eventCount++; if (eventCount>2 || !IJ.isMacOSX() && eventCount>1) changes = true; if (IJ.isMacOSX()) // screen update bug work around ta.setCaretPosition(ta.getCaretPosition()); } public void itemStateChanged(ItemEvent e) { CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); setFont(); } /** Override windowActivated in PlugInFrame to prevent Mac menu bar from being installed. */ public void windowActivated(WindowEvent e) { WindowManager.setWindow(this); } public void windowClosing(WindowEvent e) { close(); } /** Overrides close() in PlugInFrame. */ public void close() { boolean okayToClose = true; ImageJ ij = IJ.getInstance(); if (!getTitle().equals("Errors") && changes && !IJ.isMacro() && ij!=null && !ij.quitting()) { String msg = "Save changes to \"" + getTitle() + "\"?"; YesNoCancelDialog d = new YesNoCancelDialog(this, "Editor", msg); if (d.cancelPressed()) okayToClose = false; else if (d.yesPressed()) save(); } if (okayToClose) { setVisible(false); dispose(); WindowManager.removeWindow(this); nWindows--; } } void saveAs() { String name1 = getTitle(); if (name1.indexOf(".")==-1) name1 += ".txt"; if (defaultDir==null) { if (name1.endsWith(".txt")||name1.endsWith(".ijm")) defaultDir = Menus.getMacrosPath(); else defaultDir = Menus.getPlugInsPath(); } SaveDialog sd = new SaveDialog("Save As...", defaultDir, name1, null); String name2 = sd.getFileName(); String dir = sd.getDirectory(); if (name2!=null) { if (name2.endsWith(".java")) updateClassName(name1, name2); path = dir+name2; save(); changes = false; setWindowTitle(name2); } } /** Changes a plugins class name to reflect a new file name. */ public void updateClassName(String oldName, String newName) { if (newName.indexOf("_")<0) IJ.showMessage("Plugin Editor", "Plugins without an underscore in their name will not\n" +"be automatically installed when ImageJ is restarted."); if (oldName.equals(newName) || !oldName.endsWith(".java") || !newName.endsWith(".java")) return; oldName = oldName.substring(0,oldName.length()-5); newName = newName.substring(0,newName.length()-5); String text1 = ta.getText(); int index = text1.indexOf("public class "+oldName); if (index<0) return; String text2 = text1.substring(0,index+13)+newName+text1.substring(index+13+oldName.length(),text1.length()); ta.setText(text2); } void find(String s) { if (s==null) { GenericDialog gd = new GenericDialog("Find", this); gd.addStringField("Find: ", searchString, 20); String[] labels = {"Case Sensitive", "Whole Words"}; boolean[] states = {caseSensitive, wholeWords}; //boolean[] states = new boolean[2]; //states[0]=caseSensitive; states[1]=wholeWords; gd.addCheckboxGroup(1, 2, labels, states); gd.showDialog(); if (gd.wasCanceled()) return; s = gd.getNextString(); caseSensitive = gd.getNextBoolean(); wholeWords = gd.getNextBoolean(); } if (s.equals("")) return; String text = ta.getText(); String s2 = s; if (!caseSensitive) { text = text.toLowerCase(Locale.US); s = s.toLowerCase(Locale.US); } int index = -1; if (wholeWords) { int position = ta.getCaretPosition()+1; while (true) { index = text.indexOf(s, position); if (index==-1) break; if (isWholeWordMatch(text, s, index)) break; position = index + 1; if (position>=text.length()-1) {index=-1; break;} } } else index = text.indexOf(s, ta.getCaretPosition()+1); searchString = s2; if (index<0) {IJ.beep(); return;} ta.setSelectionStart(index); ta.setSelectionEnd(index+s.length()); } boolean isWholeWordMatch(String text, String word, int index) { char c = index==0?' ':text.charAt(index-1); if (Character.isLetterOrDigit(c) || c=='_') return false; c = index+word.length()>=text.length()?' ':text.charAt(index+word.length()); if (Character.isLetterOrDigit(c) || c=='_') return false; return true; } void gotoLine() { GenericDialog gd = new GenericDialog("Go to Line", this); gd.addNumericField("Go to line number: ", lineNumber, 0); gd.showDialog(); if (gd.wasCanceled()) return; int n = (int)gd.getNextNumber(); if (n<1) return; String text = ta.getText(); char[] chars = new char[text.length()]; chars = text.toCharArray(); int count=1, loc=0; for (int i=0; i<chars.length; i++) { if (chars[i]=='\n') count++; if (count==n) {loc=i+1; break;} } ta.setCaretPosition(loc); lineNumber = n; } void zapGremlins() { String text = ta.getText(); char[] chars = new char[text.length()]; chars = text.toCharArray(); int count=0; boolean inQuotes = false; char quoteChar = 0; for (int i=0; i<chars.length; i++) { char c = chars[i]; if (!inQuotes && (c=='"' || c=='\'')) { inQuotes = true; quoteChar = c; } else { if (inQuotes && (c==quoteChar || c=='\n')) inQuotes = false; } if (!inQuotes && c!='\n' && c!='\t' && (c<32||c>127)) { count++; chars[i] = ' '; //IJ.log(""+(0+c)); } } if (count>0) { text = new String(chars); ta.setText(text); } if (count>0) IJ.showMessage("Zap Gremlins", count+" invalid characters converted to spaces"); else IJ.showMessage("Zap Gremlins", "No invalid characters found"); } void selectAll() { ta.selectAll(); } void convertToPlugin() { if (!(getTitle().endsWith(".txt")||getTitle().endsWith(".ijm"))) return; String text = ta.getText(); if (text==null || text.equals("")) { IJ.runPlugIn("ij.plugin.NewPlugin", " "); return; } if (text.indexOf("{")>-1) { IJ.showMessage("Convert to Plugin", "Conversion limited to recorder generated macro code."); return; } StringTokenizer st = new StringTokenizer(text, "\n"); int n = st.countTokens(); String line; StringBuffer sb = new StringBuffer(); for(int i=0; i<n; i++) { line = st.nextToken(); if (line!=null && line.length()>3) { if (line.equals("close();")) line = "run(\"Close\");"; sb.append("\t\tIJ."); if (line.startsWith("//run")) line = line.substring(2); sb.append(line); sb.append('\n'); } } NewPlugin np = (NewPlugin)IJ.runPlugIn("ij.plugin.NewPlugin", new String(sb)); Editor ed = np.getEditor(); String title = getTitle(); if (title.equals("Macro.txt")||title.equals("Macro.ijm")) title = "Converted_Macro"; if (title.endsWith(".txt")||title.endsWith(".ijm")) title = title.substring(0, title.length()-4); if (title.indexOf('_')==-1) title += "_"; title += ".java"; ed.updateClassName(ed.getTitle(), title); ed.setTitle(title); } void changeFontSize(boolean larger) { int in = fontSize; if (larger) { fontSize++; if (fontSize==sizes.length) fontSize = sizes.length-1; } else { fontSize--; if (fontSize<0) fontSize = 0; } IJ.showStatus(sizes[fontSize]+" point"); setFont(); } void saveSettings() { Prefs.set(FONT_SIZE, fontSize); Prefs.set(FONT_MONO, monospaced.getState()); IJ.showStatus("Font settings saved (size="+sizes[fontSize]+", monospaced="+monospaced.getState()+")"); } void setFont() { ta.setFont(new Font(getFontName(), Font.PLAIN, sizes[fontSize])); } String getFontName() { return monospaced.getState()?"Monospaced":"SansSerif"; } public void setFont(Font font) { ta.setFont(font); } public void append(String s) { ta.append(s); } public void setIsMacroWindow(boolean mw) { isMacroWindow = mw; } public static void setDefaultDirectory(String defaultDirectory) { defaultDir = defaultDirectory; } //public void keyReleased(KeyEvent e) {} //public void keyTyped(KeyEvent e) {} public void lostOwnership (Clipboard clip, Transferable cont) {} }
|
Editor |
|