/*******************************************************************************
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* 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 hr.fer.zemris.vhdllab.applets.texteditor;
import hr.fer.zemris.vhdllab.applets.texteditor.misc.KeywordHighlighterTextPane;
import hr.fer.zemris.vhdllab.applets.texteditor.misc.LineNumbers;
import hr.fer.zemris.vhdllab.applets.texteditor.misc.ModificationListener;
import hr.fer.zemris.vhdllab.entity.File;
import hr.fer.zemris.vhdllab.platform.manager.editor.impl.AbstractEditor;
import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.Highlighter;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import org.springframework.richclient.application.Application;
// Pogledaj: http://download.oracle.com/javase/tutorial/uiswing/components/generaltext.html#undo
// http://spring-rich-c.sourceforge.net/1.1.0/apidocs/org/springframework/binding/value/CommitTrigger.html
public class TextEditor extends AbstractEditor implements CaretListener, ModificationListener {
private CustomJTextPane textPane;
//private CommitTrigger commitTrigger;
private Object highlighted;
@Override
protected JComponent doInitWithoutData() {
textPane = new CustomJTextPane(this);
wrapInScrollPane = false;
JScrollPane jsp = new JScrollPane(textPane);
LineNumbers.createInstance(textPane, jsp, 30);
Document document = textPane.getDocument();
if (document instanceof AbstractDocument) {
((AbstractDocument) document).setDocumentFilter(new DocumentFilter() {
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
if (text != null && (text.length() - length) > 10 && !text.equals(CustomJTextPane.getClipboardText()) && !textIsBlank(text)) {
informPastingIsDisabled();
} else {
super.replace(fb, offset, length, text, attrs);
}
}
private boolean textIsBlank(String text) {
if(text==null) return true;
for(char c : text.toCharArray()) {
switch(c) {
case ' ': break;
case '\t': break;
case '\r': break;
case '\n': break;
default: return false;
}
}
return true;
}
private void informPastingIsDisabled() {
JFrame frame = Application.instance().getActiveWindow().getControl();
JOptionPane.showMessageDialog(frame, "Pasting text from outside of vhdllab is disabled!", "Paste text",
JOptionPane.INFORMATION_MESSAGE);
}
});
}
textPane.addCaretListener(this);
// commitTrigger = new CommitTrigger();
// TextComponentPopup.attachPopup(textPane, commitTrigger);
return jsp;
}
@Override
protected void doInitWithData(File f) {
textPane.setText(f.getData());
textPane.resetUndoManager();
// commitTrigger.commit();
}
@Override
protected String getData() {
return textPane.getText();
}
@Override
protected void doDispose() {
}
@Override
public void setEditable(boolean flag) {
textPane.setEditable(flag);
}
@Override
public void highlightLine(int line) {
int caret = textPane.getCaretPosition();
Highlighter h = textPane.getHighlighter();
h.removeAllHighlights();
String content = textPane.getText();
textPane.setCaretPosition(caret);
int pos = 0;
line--;
while (line > 0) {
pos = content.indexOf('\n', pos) + 1;
line--;
}
int last = content.indexOf('\n', pos) + 1;
if (last == 0) {
last = content.length();
}
try {
highlighted = h.addHighlight(pos, last, new DefaultHighlighter.DefaultHighlightPainter(new Color(180, 210,
238)));
} catch (BadLocationException e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
JOptionPane.showMessageDialog(null, sw.toString());
}
}
//
// ModificationListener implementation
//
@Override
public void contentModified() {
setModified(true);
}
//
// CaretListener implementation
//
@Override
public void caretUpdate(CaretEvent e) {
if (highlighted != null) {
textPane.getHighlighter().removeHighlight(highlighted);
highlighted = null;
}
}
/**
* Custom razred koji omogucava presretanje copy&paste operacija.
* Interno, u globalnu se varijablu pamti sve sto je korisnik
* kopirao/izrezao iz uredivaca tako da se pastanje toga dozvoljava
* ali pastanje bilo cega drugoga veceg od 10 znakova ne.
* Dodatno, font se postavlja na monospaced sto je uobicajeno
* za uredivace koda.
*/
private static class CustomJTextPane extends KeywordHighlighterTextPane {
private static final long serialVersionUID = 1L;
private static String clipboardText;
private ModificationListener modificationListener;
protected UndoManager undoManager;
protected UndoAction undoAction;
protected RedoAction redoAction;
public CustomJTextPane(ModificationListener modificationListener) {
this.modificationListener = modificationListener;
Font font = this.getFont();
int fontSize = font.getSize();
Font newFont = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
this.setFont(newFont);
undoManager = new UndoManager();
undoAction = new UndoAction();
redoAction = new RedoAction();
InputMap inputMap = getInputMap();
KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK);
inputMap.put(undoKeyStroke, undoAction);
KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, Event.CTRL_MASK);
inputMap.put(redoKeyStroke, redoAction);
getDocument().addUndoableEditListener(new UndoableEditListener() {
@Override
public void undoableEditHappened(UndoableEditEvent e) {
if(analysisInProgress) return;
undoManager.addEdit(e.getEdit());
undoAction.updateUndoState();
redoAction.updateRedoState();
}
});
getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
if(!analysisInProgress && CustomJTextPane.this.modificationListener!=null) {
CustomJTextPane.this.modificationListener.contentModified();
}
}
@Override
public void insertUpdate(DocumentEvent e) {
if(!analysisInProgress && CustomJTextPane.this.modificationListener!=null) {
CustomJTextPane.this.modificationListener.contentModified();
}
}
@Override
public void changedUpdate(DocumentEvent e) {
if(!analysisInProgress && CustomJTextPane.this.modificationListener!=null) {
CustomJTextPane.this.modificationListener.contentModified();
}
}
});
}
@Override
public void copy() {
String selText = this.getSelectedText();
if(selText==null || selText.isEmpty()) {
return;
}
setClipboardText(selText);
super.copy();
}
@Override
public void cut() {
String selText = this.getSelectedText();
if(selText==null || selText.isEmpty()) {
return;
}
setClipboardText(selText);
super.cut();
}
public void resetUndoManager() {
undoManager.discardAllEdits();
undoAction.updateUndoState();
redoAction.updateRedoState();
}
public static synchronized String getClipboardText() {
return clipboardText;
}
public static synchronized void setClipboardText(String clipboardText) {
CustomJTextPane.clipboardText = clipboardText;
}
class UndoAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public UndoAction() {
super("Undo");
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
try {
undoManager.undo();
} catch (CannotUndoException ex) {
System.out.println("Unable to undo: " + ex);
ex.printStackTrace();
}
updateUndoState();
redoAction.updateRedoState();
}
protected void updateUndoState() {
if (undoManager.canUndo()) {
setEnabled(true);
putValue(Action.NAME, undoManager.getUndoPresentationName());
} else {
setEnabled(false);
putValue(Action.NAME, "Undo");
}
}
}
class RedoAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public RedoAction() {
super("Redo");
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
try {
undoManager.redo();
} catch (CannotRedoException ex) {
System.out.println("Unable to redo: " + ex);
ex.printStackTrace();
}
updateRedoState();
undoAction.updateUndoState();
}
protected void updateRedoState() {
if (undoManager.canRedo()) {
setEnabled(true);
putValue(Action.NAME, undoManager.getRedoPresentationName());
} else {
setEnabled(false);
putValue(Action.NAME, "Redo");
}
}
}
}
}