/* EditorFilter.java * This file is part of source files of MEPer. * It defines the behaviors of the editor. * * @author Shengjun Pan */ import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; import java.util.*; import javax.swing.undo.*; public class EditorFilter extends DocumentFilter { String[] keywords ={"beginfig","endfig","for","draw","drawarrow","endfor", "def", "vardef", "enddef","if", "fi", "withcolor","fill", "shifted", "scaled", "rotated", "withpen"}; String[] datatypes = {"picture","numeric","path","pair","transform","color", "string", "boolean", "pen"}; int fontSize = 14; HashMap tokenToAset; JTextPane editor; JButton saveButton; public EditorFilter(JTextPane ed, JButton sb) { editor = ed; saveButton = sb; tokenToAset = new HashMap(); SimpleAttributeSet aset= new SimpleAttributeSet(); StyleConstants.setForeground(aset, Color.BLUE); StyleConstants.setBold(aset, true); StyleConstants.setFontSize(aset,fontSize); tokenToAset.put("keyword", aset); aset= new SimpleAttributeSet(); StyleConstants.setForeground(aset, Color.GREEN); StyleConstants.setBold(aset, true); StyleConstants.setFontSize(aset,fontSize); tokenToAset.put("digits", aset); aset= new SimpleAttributeSet(); StyleConstants.setForeground(aset, Color.BLACK); StyleConstants.setBold(aset, false); StyleConstants.setFontSize(aset,fontSize); tokenToAset.put("normal", aset); aset= new SimpleAttributeSet(); StyleConstants.setForeground(aset, Color.GREEN); StyleConstants.setBold(aset, true); StyleConstants.setFontSize(aset,fontSize); tokenToAset.put("datatype", aset); aset= new SimpleAttributeSet(); StyleConstants.setForeground(aset, Color.MAGENTA); StyleConstants.setBold(aset, true); StyleConstants.setFontSize(aset,fontSize); tokenToAset.put("quoted", aset); //editor.addKeyListener(this); editor.setFont(new Font(Font.MONOSPACED,Font.PLAIN,fontSize)); // enable undo/redo final UndoManager undoManager = new UndoManager(); editor.getDocument().addUndoableEditListener(undoManager); editor.getInputMap().put(KeyStroke.getKeyStroke("ctrl Z"), "undo"); editor.getActionMap().put("undo", new AbstractAction() { public void actionPerformed(ActionEvent evt) { try { undoManager.undo();} catch (Throwable t) {Toolkit.getDefaultToolkit().beep();} }}); editor.getInputMap().put(KeyStroke.getKeyStroke("ctrl R"), "redo"); editor.getActionMap().put("redo", new AbstractAction() { public void actionPerformed(ActionEvent evt) { try { undoManager.redo();} catch (Throwable t) {Toolkit.getDefaultToolkit().beep();} }}); } @Override public void insertString(FilterBypass fb, int offs, String str, AttributeSet a) throws BadLocationException { saveButton.setEnabled(true); highlightAndInsert(fb, offs, str); } @Override public void replace(DocumentFilter.FilterBypass fb, int offs, int len, String str, AttributeSet a) throws BadLocationException { saveButton.setEnabled(true); super.remove(fb, offs, len); highlightAndInsert(fb,offs,str); } @Override public void remove(DocumentFilter.FilterBypass fb, int offs, int len) throws BadLocationException { saveButton.setEnabled(true); boolean removingWhite = fb.getDocument().getText(offs, len).trim().isEmpty(); super.remove(fb, offs, len); String indent = ""; String smallerIndent =""; try { indent = indent(offs,false); smallerIndent = indent(offs,true); int newLineOffs = offs - indent.length()-1; String maybeNewline=newLineOffs<0?"":editor.getDocument().getText(newLineOffs, 1); if(removingWhite && indent.length()>0 && (maybeNewline.isEmpty() || maybeNewline.equals("\n"))){ super.remove(fb,newLineOffs+1, indent.length()); super.insertString(fb,newLineOffs+1,smallerIndent, tokenToAset.get("normal")); } else if(offs>0) { highlightAndInsert(fb,offs,"");} } catch(Throwable t) {} } private void highlightAndInsert(FilterBypass fb, int offs, String str) throws BadLocationException { // invoke automatical indentation only if a single is received. // In the case of pasting multiple lines, the original indentation // should be reserved. if(str.length()==1 && str.charAt(0)=='\n') { String indent = ""; try { indent = indent(offs,false); super.insertString(fb,offs, "\n"+indent, tokenToAset.get("normal")); } catch(Throwable t) {} return; } //translate String newstr=""; for(int i=0;i=0 ) { char c = fb.getDocument().getText(from, 1).charAt(0); if(Character.isWhitespace(c)){ break;} from--; } from ++; } prefix = fb.getDocument().getText(from, offs-from); // search for partial word after int to=offs; if (newstr.isEmpty() || !Character.isWhitespace(newstr.charAt(newstr.length()-1))){ int wholeLen = fb.getDocument().getLength(); while(to < wholeLen) { char c = fb.getDocument().getText(to,1).charAt(0); if(Character.isWhitespace(c)){break;} to++; } } suffix = fb.getDocument().getText(offs,to-offs); // removing the partial words offs -= prefix.length(); super.remove(fb, offs, prefix.length()+suffix.length()); String[] words = newstr.split(" ",-1); words[0] = prefix + words[0]; words[words.length-1] += suffix; newstr = prefix + newstr + suffix; String state="normal"; if(offs>0){ Color fgcolor=StyleConstants.getForeground(editor.getStyledDocument().getCharacterElement(offs-1).getAttributes()); if (fgcolor.equals(StyleConstants.getForeground(tokenToAset.get("quoted")))) state = "quoted"; } String token=""; char c; newstr += '\0'; for(int i=0;i< newstr.length();i++){ c = newstr.charAt(i); String newstate="normal"; if(c=='\0') newstate = "done"; else if(state.equals("quoted")|| state.equals("lquote")) newstate= (c=='\"')? "rquote":"quoted"; else if(Character.isDigit(c)) newstate = "digits"; else if(Character.isLetter(c)) newstate = "word"; else if(c=='\"') newstate= "lquote"; if (!state.equals(newstate)) { String key="normal"; if(state.equals("digits")) key="digits"; else if(state.equals("word") && isKeyword(token)) key="keyword"; else if(state.equals("word") && isDatatype(token)) key="datatype"; else if(state.equals("quoted")) key="quoted"; super.insertString(fb, offs, token, tokenToAset.get(key)); offs += token.length(); state = newstate; token = ""; } token +=c; } editor.setCaretPosition(offs-suffix.length()); } // return the indent (as string of whitespaces) of the current line // or closest shorter indent of previous lines if 'wantShorter' is true. private String indent(int offs, boolean wantShorter) throws BadLocationException { int pos=offs-1; int pos_firstNonWhite = offs; int firstIndentSize=-1; while(pos>=0){ char ch= editor.getText(pos, 1).charAt(0); if(ch=='\n') { int previousIndentSize = pos_firstNonWhite-pos-1; if(!wantShorter || previousIndentSize < firstIndentSize) break; if(firstIndentSize<0) firstIndentSize = previousIndentSize; pos_firstNonWhite = pos; } if(!Character.isWhitespace(ch)) { pos_firstNonWhite = pos; } pos --; } return editor.getText(pos+1, pos_firstNonWhite-pos-1); } private boolean isKeyword(String word) { for(int i=0;i