/* * Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.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 com.igormaznitsa.prol.easygui; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.event.*; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.prefs.Preferences; import javax.swing.*; import javax.swing.border.TitledBorder; import javax.swing.event.TreeModelListener; import javax.swing.tree.*; /** * The class extends a Scroll pane and is used as the ancestor for all text * windows in the IDE because it is a very specialized auxiliary class, it is * not described very precisely * * @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com) */ public abstract class AbstractProlEditor extends JPanel implements TreeModel { private static final long serialVersionUID = -3683279049571011888L; public final static class PropertyLink { private final Object ownerObject; private final Class<?> ownerClass; private final String name; private final String property; public PropertyLink(final Object object, final String name, final String property) { this.name = name; this.ownerObject = object; this.ownerClass = object.getClass(); this.property = property; } public Object getOwner() { return ownerObject; } @SuppressWarnings("unchecked") public Object getProperty() { try { Method meth = ownerClass.getMethod("get" + property, (Class[]) null); return meth.invoke(ownerObject); } catch (Throwable thr) { throw new RuntimeException("Can't read property", thr); } } @SuppressWarnings("unchecked") public void setProperty(final Object obj) { try { ownerClass.getMethod("set" + property, new Class<?>[]{obj.getClass()}).invoke(ownerObject, obj); } catch (Throwable thr) { throw new RuntimeException("Can't set property", thr); } } @Override public String toString() { String str = this.name; final Object val = getProperty(); if (val instanceof Boolean) { str += " (" + val.toString() + ")"; } return str; } } protected final ArrayList<PropertyLink> editableProperties; protected final EditorPane editor; protected final JScrollPane scrollPane; protected final JPopupMenu POPUP_MENU = new JPopupMenu(); protected final JMenuItem POPUP_CLEARTEXT = new JMenuItem("Clear text",UIUtils.loadIcon("page_delete")); protected final JMenuItem POPUP_COPY = new JMenuItem("Copy",UIUtils.loadIcon("page_copy")); protected final JMenuItem POPUP_CUT = new JMenuItem("Cut",UIUtils.loadIcon("cut")); protected final JMenuItem POPUP_PASTE = new JMenuItem("Paste",UIUtils.loadIcon("page_paste")); private final String nameID; private boolean wordWrap; protected static Color extractColor(final Preferences prefs, final String name) { final int value = prefs.getInt(name, 0xFFFFFFFF); return value == 0xFFFFFFFF ? null : new Color(value & 0xFFFFFF); } protected static Color extractColor(final Preferences prefs, final String name, final Color dflt) { final int value = prefs.getInt(name, 0xFFFFFFFF); return value == 0xFFFFFFFF ? dflt : new Color(value & 0xFFFFFF); } public String getNameID() { return this.nameID; } public Font getEdFont() { return editor.getFont(); } public void setPopupMenu(final JPopupMenu menu){ this.editor.setPopupMenu(menu); } public void setEdFont(final Font val) { editor.setFont(val); } public Color getEdBackground() { return editor.getBackground(); } public void setEdBackground(final Color val) { editor.setBackground(val); scrollPane.getViewport().setBackground(val); } public boolean getEdWordWrap() { return this.wordWrap; } public void setEdWordWrap(final Boolean value) { final Component c = this.scrollPane.getViewport().getView(); if (c != null) { this.scrollPane.getViewport().remove(c); } final Container editorParent = this.editor.getParent(); if (editorParent != null) { editorParent.remove(this.editor); } this.wordWrap = value; if (value) { this.scrollPane.getViewport().setView(this.editor); } else { final JPanel wrapper = new JPanel(new BorderLayout(0, 0)); wrapper.add(this.editor, BorderLayout.CENTER); this.scrollPane.getViewport().setView(wrapper); } this.scrollPane.revalidate(); this.scrollPane.repaint(); } public Color getEdForeground() { return editor.getForeground(); } public void setEdForeground(final Color val) { editor.setForeground(val); } public Color getEdCaretColor() { return editor.getCaretColor(); } public void setEdCaretColor(final Color val) { editor.setCaretColor(val); } protected synchronized void removePropertyFromList(final String propertyName) { for (int li = 0; li < editableProperties.size(); li++) { final PropertyLink link = editableProperties.get(li); if (link.property.equals(propertyName)) { editableProperties.remove(li); return; } } } protected final synchronized void addPropertyToList(final PropertyLink link) { this.editableProperties.add(link); } public AbstractProlEditor(final String title) { super(); this.editableProperties = new ArrayList<PropertyLink>(); this.editor = new EditorPane(); this.scrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); this.setEdWordWrap(false); setBorder(new TitledBorder(title)); this.nameID = title; this.editor.setEditable(true); setLayout(new BorderLayout(0, 0)); add(this.scrollPane, BorderLayout.CENTER); // add properties addPropertyToList(new PropertyLink(this, "Font", "EdFont")); addPropertyToList(new PropertyLink(this, "Background color", "EdBackground")); addPropertyToList(new PropertyLink(this, "Foreground color", "EdForeground")); addPropertyToList(new PropertyLink(this, "Caret color", "EdCaretColor")); addPropertyToList(new PropertyLink(this, "Word wrap", "EdWordWrap")); this.POPUP_CLEARTEXT.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { clearText(); } }); this.POPUP_COPY.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { copyText(); } }); this.POPUP_PASTE.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { pasteText(); } }); this.POPUP_CUT.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cutText(); } }); // add popup if (doesSupportTextCut()) { this.POPUP_MENU.add(this.POPUP_CUT); } this.POPUP_MENU.add(this.POPUP_COPY); if (doesSupportTextPaste()) { POPUP_MENU.add(this.POPUP_PASTE); } this.POPUP_MENU.add(this.POPUP_CLEARTEXT); this.editor.setPopupMenu(this.POPUP_MENU); } @Override public String toString() { return this.nameID; } public synchronized void setTitle(String title) { ((TitledBorder) getBorder()).setTitle(title); } @Override public void setEnabled(final boolean flag) { //super.setEnabled(flag); editor.setEditable(flag); if (!flag) { editor.transferFocus(); } } @Override public void requestFocus() { if (editor.isEditable()) { editor.requestFocus(); } } public synchronized JEditorPane getEditor() { return editor; } public synchronized void clearText() { editor.setText(""); } public static String escapeHTML(String s) { final StringBuilder sb = new StringBuilder(); int n = s.length(); for (int i = 0; i < n; i++) { char c = s.charAt(i); switch (c) { case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '&': sb.append("&"); break; case '"': sb.append("""); break; case 'à': sb.append("à"); break; case 'À': sb.append("À"); break; case 'â': sb.append("â"); break; case 'Â': sb.append("Â"); break; case 'ä': sb.append("ä"); break; case 'Ä': sb.append("Ä"); break; case 'å': sb.append("å"); break; case 'Å': sb.append("Å"); break; case 'æ': sb.append("æ"); break; case 'Æ': sb.append("Æ"); break; case 'ç': sb.append("ç"); break; case 'Ç': sb.append("Ç"); break; case 'é': sb.append("é"); break; case 'É': sb.append("É"); break; case 'è': sb.append("è"); break; case 'È': sb.append("È"); break; case 'ê': sb.append("ê"); break; case 'Ê': sb.append("Ê"); break; case 'ë': sb.append("ë"); break; case 'Ë': sb.append("Ë"); break; case 'ï': sb.append("ï"); break; case 'Ï': sb.append("Ï"); break; case 'ô': sb.append("ô"); break; case 'Ô': sb.append("Ô"); break; case 'ö': sb.append("ö"); break; case 'Ö': sb.append("Ö"); break; case 'ø': sb.append("ø"); break; case 'Ø': sb.append("Ø"); break; case 'ß': sb.append("ß"); break; case 'ù': sb.append("ù"); break; case 'Ù': sb.append("Ù"); break; case 'û': sb.append("û"); break; case 'Û': sb.append("Û"); break; case 'ü': sb.append("ü"); break; case 'Ü': sb.append("Ü"); break; case '®': sb.append("®"); break; case '©': sb.append("©"); break; case '€': sb.append("€"); break; // be carefull with this one (non-breaking whitee space) //case ' ': // sb.append(" "); // break; default: sb.append(c); break; } } return sb.toString(); } public synchronized void copyText() { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); TransferHandler transferHandler = editor.getTransferHandler(); transferHandler.exportToClipboard(editor, clipboard, TransferHandler.COPY); } public synchronized void pasteText() { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); TransferHandler transferHandler = editor.getTransferHandler(); transferHandler.importData(editor, clipboard.getContents(null)); } public synchronized void cutText() { copyText(); editor.replaceSelection(""); } @Override public void addTreeModelListener(final TreeModelListener l) { } @Override public Object getChild(final Object parent, final int index) { if (this.equals(parent)) { return editableProperties.get(index); } return null; } @Override public int getChildCount(final Object parent) { if (this.equals(parent)) { return editableProperties.size(); } return -1; } @Override public int getIndexOfChild(final Object parent, final Object child) { if (this.equals(parent)) { return editableProperties.indexOf(child); } return -1; } @Override public Object getRoot() { return this; } @Override public boolean isLeaf(final Object node) { return node instanceof PropertyLink; } @Override public void removeTreeModelListener(TreeModelListener l) { // just ignore } @Override public void valueForPathChanged(final TreePath path, final Object newValue) { // just ignore } public abstract void loadPreferences(Preferences prefs); public abstract void savePreferences(Preferences prefs); public void saveFontToPrefs(final Preferences prefs, final String key, final Font font) { String style = "PLAIN"; switch (font.getStyle()) { case Font.BOLD: { style = "BOLD"; } break; case Font.ITALIC: { style = "ITALIC"; } break; case Font.BOLD | Font.ITALIC: { style = "BOLDITALIC"; } break; } prefs.put(key, font.getFamily() + " " + style + " " + font.getSize()); } public Font loadFontFromPrefs(Preferences prefs, String key) { return Font.decode(prefs.get(key, null)); } public boolean doesSupportTextPaste() { return false; } public boolean doesSupportTextCut() { return false; } }