001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.ui; 030 031import javax.swing.JPanel; 032import javax.swing.JPopupMenu; 033import javax.swing.JScrollPane; 034import javax.swing.event.UndoableEditEvent; 035import javax.swing.event.UndoableEditListener; 036import javax.swing.text.JTextComponent; 037import javax.swing.undo.UndoManager; 038 039import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 040import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 041import org.fife.ui.rtextarea.RTextScrollPane; 042 043/** 044 * A bare-bones text editor that can do relatively robust syntax highlighting 045 * of Jython code. 046 * 047 * <p>This class relies <i>very heavily</i> upon the wonderful 048 * <a href="https://github.com/bobbylight/RSyntaxTextArea">RSyntaxTextArea</a> 049 * project.</p> 050 */ 051public class JythonEditor implements UndoableEditListener { 052 053 /** Text area that contains the syntax-highlighted text. */ 054 private final McvJythonTextArea textArea; 055 056 /** Scroll pane for {@link #textArea}. */ 057 private final RTextScrollPane scrollPane; 058 059 /** Undo manager. */ 060 private final UndoManager undo; 061 062 /** 063 * Creates a new JythonEditor. 064 */ 065 public JythonEditor() { 066 textArea = new McvJythonTextArea(20, 60); 067 068 textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_PYTHON); 069 070 textArea.setCodeFoldingEnabled(true); 071 textArea.setWhitespaceVisible(true); 072 073 // hrm? 074 textArea.setBracketMatchingEnabled(true); 075 textArea.setPaintMatchedBracketPair(true); 076 077 textArea.setAnimateBracketMatching(true); 078 textArea.setPaintTabLines(true); 079 textArea.setTabsEmulated(true); 080 textArea.setTabSize(4); 081 082 scrollPane = new RTextScrollPane(textArea); 083 084 undo = new UndoManager(); 085 086 addUndoableEditListener(this); 087 } 088 089 /** 090 * Returns the text area responsible for syntax highlighting. 091 * 092 * @return Reference to {@link #textArea}. 093 */ 094 public JTextComponent getTextComponent() { 095 return textArea; 096 } 097 098 /** 099 * Returns the {@code JScrollPane} that contains {@link #textArea}. 100 * 101 * @return {@code JScrollPane} with the text area. Suitable for adding to 102 * a {@code JPanel}. 103 */ 104 public JScrollPane getScrollPane() { 105 return scrollPane; 106 } 107 108 /** 109 * Sets the text of this document to the given {@code String}. 110 * 111 * @param text New text to use in {@link #textArea}. 112 */ 113 public void setText(String text) { 114 textArea.setText(text); 115 } 116 117 /** 118 * Returns a string containing the text of {@link #textArea}. 119 * 120 * @return The current contents of the text area. 121 */ 122 public String getText() { 123 return textArea.getText(); 124 } 125 126 /** 127 * Controls whether or not changes can be made to the contents of 128 * {@link #textArea}. 129 * 130 * @param enabled {@code true} if the editor should be enabled, 131 * {@code false} otherwise. 132 */ 133 public void setEnabled(boolean enabled) { 134 textArea.setEnabled(enabled); 135 scrollPane.getGutter().setEnabled(enabled); 136 scrollPane.getGutter().setBackground(textArea.getBackground()); 137 } 138 139 /** 140 * Returns the component (aka {@literal "the gutter"} that contains 141 * optional information like line numbers. 142 * 143 * @return {@code JPanel} that contains the line numbers. 144 */ 145 public JPanel getLineNumberComponent() { 146 return scrollPane.getGutter(); 147 } 148 149 /** 150 * Copies selected text into system clipboard. 151 */ 152 public void copy() { 153 textArea.copy(); 154 } 155 156 /** 157 * Whether or not changes can be made to {@link #textArea}. 158 * 159 * @return {@code true} if changes are allowed, {@code false} otherwise. 160 */ 161 public boolean isEnabled() { 162 return textArea.isEnabled(); 163 } 164 165 /** 166 * Insert the given text at the caret. 167 * 168 * @param textToInsert Text to insert. 169 */ 170 public void insertText(String textToInsert) { 171 int pos = textArea.getCaretPosition(); 172 String t = textArea.getText(); 173 t = t.substring(0, pos) + textToInsert + t.substring(pos); 174 textArea.setText(t); 175 textArea.setCaretPosition(pos + textToInsert.length()); 176 } 177 178 /** 179 * Handles undoable edits 180 * 181 * @param e Event that represents the undoable edit. 182 */ 183 @Override public void undoableEditHappened(UndoableEditEvent e) { 184 if (e.getEdit().isSignificant()) { 185 undo.addEdit(e.getEdit()); 186 } 187 } 188 189 /** 190 * Adds the given undoable edit listener to {@link #textArea}. 191 * 192 * @param l Listener to add. 193 */ 194 public void addUndoableEditListener(UndoableEditListener l) { 195 textArea.getDocument().addUndoableEditListener(l); 196 } 197 198 /** 199 * Remove the given undoable edit listener from {@link #textArea}. 200 * 201 * @param l Listener to remove. 202 */ 203 public void removeUndoableEditListener(UndoableEditListener l) { 204 textArea.getDocument().removeUndoableEditListener(l); 205 } 206 207 /** 208 * Returns the default {@link JPopupMenu} created by 209 * {@link RSyntaxTextArea#createPopupMenu()}. 210 * 211 * @return Popup menu. 212 */ 213 public JPopupMenu createPopupMenu() { 214 return textArea.makePopupMenu(); 215 } 216 217 public static class McvJythonTextArea extends RSyntaxTextArea { 218 219 McvJythonTextArea(int rows, int columns) { 220 super(rows, columns); 221 } 222 223 @Override protected JPopupMenu createPopupMenu() { 224 // this is needed so that the popup is disabled by default, which 225 // allows JythonManager's addMouseListener stuff to work a bit 226 // better. 227 return null; 228 } 229 230 public JPopupMenu makePopupMenu() { 231 // this method is mostly for getting around the fact that 232 // createPopupMenu is protected 233 return super.createPopupMenu(); 234 } 235 } 236}