/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ro.nextreports.designer.ui.sqleditor.syntax; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.swing.Action; import javax.swing.JEditorPane; import javax.swing.KeyStroke; import javax.swing.text.DefaultEditorKit; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; import javax.swing.text.PlainDocument; import javax.swing.text.TextAction; import javax.swing.text.View; import javax.swing.text.ViewFactory; /** * To use the package, just set the EditorKit of the EditorPane to a new * instance of this class. You need to pass a proper lexer to the class. * * @author Decebal Suiu */ public class SyntaxEditorKit extends DefaultEditorKit implements ViewFactory { /** Undo action. */ public static final String undoAction = "undo"; /** Redo action. */ public static final String redoAction = "redo"; /** Indent action. */ public static final String indentAction = "indent"; /** Unindent action. */ public static final String unindentAction = "unindent"; /** Smart indent action. */ public static final String smartIndentAction = "smart-indent"; private static final long serialVersionUID = 3971907941600240991L; private Lexer lexer; /** * Create a new Kit for the given language */ public SyntaxEditorKit(Lexer lexer) { super(); this.lexer = lexer; } @Override public ViewFactory getViewFactory() { return this; } public View create(Element element) { return new SyntaxView(element); } /** * This is called by Swing to create a Document for the JEditorPane document * This may be called before you actually get a reference to the control. * We use it here to create a properl lexer and pass it to the * SyntaxDcument we return. * @return */ @Override public Document createDefaultDocument() { return new SyntaxDocument(lexer); } @Override public void install(JEditorPane target) { super.install(target); // add key bindings Keymap keymap = target.getKeymap(); JTextComponent.loadKeymap(keymap, getKeyBindings(), target.getActions()); } /** * Get actions associated with this kit. getCustomActions() is called * to get basic list. */ public final Action[] getActions() { List<Action> actions = new ArrayList<Action>(); actions.addAll(Arrays.asList(super.getActions())); actions.addAll(Arrays.asList(getDefaultActions())); actions.addAll(Arrays.asList(getCustomActions())); return actions.toArray(new Action[actions.size()]); } protected Action[] getCustomActions() { return new Action[0]; } private Action[] getDefaultActions() { return new Action[] { new UndoAction(), new RedoAction(), new IndentAction(), new UnindentAction() }; } protected JTextComponent.KeyBinding[] getKeyBindings() { List<JTextComponent.KeyBinding> keyBindings = new ArrayList<JTextComponent.KeyBinding>(); keyBindings.addAll(Arrays.asList(getDefaultKeyBindings())); keyBindings.addAll(Arrays.asList(getCustomKeyBindings())); return keyBindings.toArray(new JTextComponent.KeyBinding[keyBindings.size()]); } protected JTextComponent.KeyBinding[] getCustomKeyBindings() { return new JTextComponent.KeyBinding[0]; } private JTextComponent.KeyBinding[] getDefaultKeyBindings() { return new JTextComponent.KeyBinding[] { new JTextComponent.KeyBinding(KeyStroke.getKeyStroke("control Z"), undoAction), new JTextComponent.KeyBinding(KeyStroke.getKeyStroke("control Y"), redoAction), new JTextComponent.KeyBinding(KeyStroke.getKeyStroke("TAB"), indentAction), new JTextComponent.KeyBinding(KeyStroke.getKeyStroke("shift TAB"), unindentAction) }; } /** * A Pair action inserts a pair of characters (left and right) around the * current selection, and then places the caret between them. */ public static class PairAction extends TextAction { private static final long serialVersionUID = 512435731659762179L; protected String left; protected String right; public PairAction(String actionName, String left, String right) { super(actionName); this.left = left; this.right = right; } public void actionPerformed(ActionEvent event) { JTextComponent target = getTextComponent(event); if (target != null) { String selected = target.getSelectedText(); if (selected != null) { target.replaceSelection(left + selected + right); } else { target.replaceSelection(left + right); } target.setCaretPosition(target.getCaretPosition() - 1); } } } /** * This action performs SmartIndentation each time VK_ENTER is pressed * SmartIndentation is inserting the same amount of spaces as * the line above. May not be too smart, but good enough. */ public static class SmartIndentAction extends TextAction { private static final long serialVersionUID = -4884630822418253474L; public SmartIndentAction() { super(smartIndentAction); } public void actionPerformed(ActionEvent event) { JTextComponent target = getTextComponent(event); if (target != null) { String line = SyntaxUtil.getLine(target); /** * Perform Smart Indentation: pos must be on a line: this method will * use the previous lines indentation (number of spaces before any non-space * character or end of line) and return that as the prefix. */ String indent = ""; if (line != null && line.length() > 0) { int i = 0; while (i < line.length() && line.charAt(i) == ' ') { i++; } indent = line.substring(0, i); } target.replaceSelection("\n" + indent); } } } /** * IndentAction is used to replace Tabs with spaces. If there is selected * text, then the lines spanning the selection will be shifted * right by one tab-width space character. */ public static class IndentAction extends DefaultEditorKit.InsertTabAction { private static final long serialVersionUID = 182313664992032966L; public IndentAction() { super(); putValue(Action.NAME, indentAction); } @Override public void actionPerformed(ActionEvent event) { JTextComponent target = getTextComponent(event); if (target != null) { String selected = target.getSelectedText(); if (selected == null) { PlainDocument pDoc = (PlainDocument) target.getDocument(); Integer tabStop = (Integer) pDoc.getProperty(PlainDocument.tabSizeAttribute); // insert needed number of tabs: int lineStart = pDoc.getParagraphElement(target.getCaretPosition()).getStartOffset(); // column int column = target.getCaretPosition() - lineStart; int needed = tabStop - (column % tabStop); target.replaceSelection(SyntaxUtil.SPACES.substring(0, needed)); } else { String[] lines = SyntaxUtil.getSelectedLines(target); int start = target.getSelectionStart(); StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append('\t'); sb.append(line); sb.append('\n'); } target.replaceSelection(sb.toString()); target.select(start, start + sb.length()); } } } } /** * This is usually mapped to Shift-TAB to unindent the selection. The * current line, or the selected lines are un-indented by the tabstop of the * document. */ public static class UnindentAction extends TextAction { private static final long serialVersionUID = -4364953875980816216L; public UnindentAction() { super(unindentAction); } public void actionPerformed(ActionEvent event) { JTextComponent target = getTextComponent(event); Integer tabStop = (Integer) target.getDocument().getProperty(PlainDocument.tabSizeAttribute); String indent = SyntaxUtil.SPACES.substring(0, tabStop); if (target != null) { String[] lines = SyntaxUtil.getSelectedLines(target); int start = target.getSelectionStart(); StringBuilder sb = new StringBuilder(); for (String line : lines) { if (line.startsWith(indent)) { sb.append(line.substring(indent.length())); } else if (line.startsWith("\t")) { sb.append(line.substring(1)); } else { sb.append(line); } sb.append('\n'); } target.replaceSelection(sb.toString()); target.select(start, start + sb.length()); } } } /** * Undo action. */ public static class UndoAction extends TextAction { private static final long serialVersionUID = -1872733699834542335L; public UndoAction() { super(undoAction); } public void actionPerformed(ActionEvent event) { JTextComponent target = getTextComponent(event); if (target != null) { if (target.getDocument() instanceof SyntaxDocument) { SyntaxDocument document = (SyntaxDocument) target.getDocument(); document.doUndo(); } } } } /** * Redo action. */ public static class RedoAction extends TextAction { private static final long serialVersionUID = 5474351789865934556L; public RedoAction() { super(redoAction); } public void actionPerformed(ActionEvent event) { JTextComponent target = getTextComponent(event); if (target != null) { if (target.getDocument() instanceof SyntaxDocument) { SyntaxDocument document = (SyntaxDocument) target.getDocument(); document.doRedo(); } } } } }