/* * Copyright 2008 Ayman Al-Sairafi ayman.alsairafi@gmail.com * * Licensed 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 jsyntaxpane; import java.util.logging.Level; import jsyntaxpane.actions.SyntaxActions; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.logging.Logger; 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.View; import javax.swing.text.ViewFactory; import jsyntaxpane.components.SyntaxComponent; import jsyntaxpane.util.Configuration; import jsyntaxpane.util.JarServiceProvider; /** * The DefaultSyntaxKit is the main entry to SyntaxPane. 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 ayman */ public class DefaultSyntaxKit extends DefaultEditorKit implements ViewFactory { public static Font DEFAULT_FONT; private static Set<String> CONTENTS = new HashSet<String>(); private Lexer lexer; private static final Logger LOG = Logger.getLogger(DefaultSyntaxKit.class.getName()); private List<SyntaxComponent> editorComponents = new ArrayList<SyntaxComponent>(); /** * Main Configuration of JSyntaxPane */ private static Configuration CONFIG; static { initKit(); } /** * Create a new Kit for the given language */ public DefaultSyntaxKit(Lexer lexer) { super(); this.lexer = lexer; } @Override public ViewFactory getViewFactory() { return this; } public View create(Element element) { return new SyntaxView(element, CONFIG, this.getClass().getSimpleName()); } /** * Install the View on the given EditorPane. This is called by Swing and * can be used to do anything you need on the JEditorPane control. Here * I set some default Actions. * * @param editorPane */ @Override public void install(JEditorPane editorPane) { super.install(editorPane); editorPane.setFont(DEFAULT_FONT); Keymap km_parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP); Keymap km_new = JTextComponent.addKeymap(null, km_parent); addKeyActions(km_new); editorPane.setKeymap(km_new); // install the components to the editor: String[] components = CONFIG.getPrefixPropertyList(this.getClass().getSimpleName(), "components"); if (components != null && components.length > 0) { for (String c : components) { try { Class<SyntaxComponent> compClass = (Class<SyntaxComponent>) Class.forName(c); SyntaxComponent comp = compClass.newInstance(); comp.config(CONFIG, this.getClass().getSimpleName()); comp.install(editorPane); editorComponents.add(comp); } catch (InstantiationException ex) { LOG.log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { LOG.log(Level.SEVERE, null, ex); } catch (ClassNotFoundException ex) { LOG.log(Level.SEVERE, null, ex); } } } } @Override public void deinstall(JEditorPane editorPane) { for (SyntaxComponent c : editorComponents) { c.deinstall(editorPane); } } /** * Add keyboard actions to this control. * @param map */ public void addKeyActions(Keymap map) { int menuShortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); map.addActionForKeyStroke(KeyStroke.getKeyStroke( KeyEvent.VK_Z, menuShortcutMask), SyntaxActions.UNDO); map.addActionForKeyStroke( KeyStroke.getKeyStroke(KeyEvent.VK_Y, menuShortcutMask), SyntaxActions.REDO); map.addActionForKeyStroke(KeyStroke.getKeyStroke("TAB"), SyntaxActions.INDENT); map.addActionForKeyStroke(KeyStroke.getKeyStroke("TAB"), SyntaxActions.INDENT); map.addActionForKeyStroke(KeyStroke.getKeyStroke("shift TAB"), SyntaxActions.UNINDENT); } /** * 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); } /** * This is called to initialize the list of lexers we have. You can call * this at initialization, or it will be called when needed. * * The method will also add the appropriate EditorKit classes to the * corresponding ContentType of the JEditorPane. After this is called, * you can simply call the editor.setCOntentType("text/java") on the * control and you will be done. */ public static void initKit() { // attempt to find a suitable default font GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fonts = ge.getAvailableFontFamilyNames(); Arrays.sort(fonts); if (Arrays.binarySearch(fonts, "Courier") >= 0) { DEFAULT_FONT = new Font("Courier", Font.PLAIN, 14); } else if (Arrays.binarySearch(fonts, "Courier new") >= 0) { DEFAULT_FONT = new Font("Courier new", Font.PLAIN, 14); } else if (Arrays.binarySearch(fonts, "Monospaced") >= 0) { DEFAULT_FONT = new Font("Monospaced", Font.PLAIN, 13); } // read the Default Kits and their associated types Properties kitsForTypes = JarServiceProvider.readProperties("jsyntaxpane.kitsfortypes"); for (Object key : kitsForTypes.keySet()) { String type = (String) key; String classname = kitsForTypes.getProperty(type); registerContentType(type, classname); } CONFIG = new Configuration(JarServiceProvider.readProperties("jsyntaxpane.config")); } /** * Register the given content type to use the given classsname as its kit * When this is called, an entry is added into the private HashMap of the * registered editorskits. This is needed so that the SyntaxPane library * has it's own registeration of all the EditorKits * @param type * @param classname */ public static void registerContentType(String type, String classname) { JEditorPane.registerEditorKitForContentType(type, classname); CONTENTS.add(type); } /** * Return all the content types supported by this library. This will be the * content types in the file WEB-INF/services/resources/jsyntaxpane.kitsfortypes * @return sorted array of all registered content types */ public static String[] getContentTypes() { String[] types = CONTENTS.toArray(new String[0]); Arrays.sort(types); return types; } /** * returns the current config * @return */ public static Configuration getConfig() { return CONFIG; } /** * Merges the given properties with the defaults, which are read from the * Jar file * @param config */ public static void setConfig(Properties config) { DefaultSyntaxKit.CONFIG.putAll(config); } }