newlisp/guiserver/java/TextPaneWidget.java

622 lines
15 KiB
Java
Raw Normal View History

2016-06-11 17:22:37 +00:00
//
// TextPaneWidget.java
// guiserver
//
// Created by Lutz Mueller on 6/11/07.
//
//
// Copyright (C) 2016 Lutz Mueller
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.event.*;
import javax.swing.undo.*;
import java.io.UnsupportedEncodingException;
@SuppressWarnings("unchecked")
public class TextPaneWidget extends aTextWidget {
JTextPane textPane;
String contentType = null;
CaretListener caretListener;
CaretEvent lastCaretEvent = null;
int lastCharCode = 65535;
int lastModifiers = 0;
int documentLength = 0;
String undoState = "nil";
String redoState = "nil";
Color foreground = new Color(0,0,0);
StyledDocument styledDoc;
Vector shTopLevels;
TextPaneWidget widget;
static final int SYNTAX_NONE = 0;
static final int SYNTAX_NEWLISP = 1;
static final int SYNTAX_C = 2;
static final int SYNTAX_CPP = 3;
static final int SYNTAX_JAVA = 4;
static final int SYNTAX_PHP = 5;
int syntaxSelected = SYNTAX_NONE;
//undo helpers
protected UndoAction undoAction;
protected RedoAction redoAction;
protected UndoManager undo = new UndoManager();
MyUndoableEditListener undoableEditListener;
boolean undoEnabled = true;
public TextPaneWidget(StringTokenizer params)
{
id = params.nextToken();
action = params.nextToken();
textPane = new JTextPane();
textPane.setDoubleBuffered(true);
Caret caret = new MyCaret();
caret.setBlinkRate(500);
textPane.setCaret(caret);
if(params.hasMoreTokens())
contentType = params.nextToken();
textPane.setContentType(contentType);
areaScrollPane = new JScrollPane(textPane);
areaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
container = areaScrollPane;
jcomponent = textPane;
component = areaScrollPane;
textcomp = textPane;
isScrollable = true;
widget = this;
shTopLevels = new Vector();
if(params.hasMoreTokens())
areaScrollPane.setPreferredSize(
new Dimension (Integer.parseInt(params.nextToken()),Integer.parseInt(params.nextToken())));
gsObject.widgets.put(id, this);
styledDoc = textPane.getStyledDocument();
AbstractDocument doc = (AbstractDocument)styledDoc;
//doc.setDocumentFilter(new MyDocumentFilter(1000000));
KeyListener keyListener = new KeyAdapter() {
public void keyPressed(KeyEvent e)
{
Character chr = new Character(e.getKeyChar());
int code = e.getKeyCode();
lastCharCode = chr.hashCode();
lastModifiers = e.getModifiersEx();
//System.out.println("->" + lastCharCode);
if(syntaxSelected != SYNTAX_NONE) colorSyntax();
switch(lastCharCode)
{
case 40:
highlightClosingPar('(', ')');
break;
case 41:
highlightOpeningPar('(', ')');
break;
case 123:
highlightClosingPar('{', '}');
break;
case 125:
highlightOpeningPar('{', '}');
break;
case 91:
highlightClosingPar('[', ']');
break;
case 93:
highlightOpeningPar('[', ']');
break;
}
}
};
caretListener = new CaretListener() {
public void caretUpdate(CaretEvent ce)
{
int mark, dot;
if(ce == null) return;
mark = ce.getMark();
dot = ce.getDot();
lastCaretEvent = ce;
undoAction.updateUndoState();
redoAction.updateRedoState();
if(undoAction.isEnabled()) undoState = "true";
else undoState = "nil";
if(redoAction.isEnabled()) redoState = "true";
else redoState = "nil";
guiserver.out.println("(" + action + " \"" + id + "\" " + lastCharCode + " " +
lastModifiers + " " + dot + " " + mark + " " + documentLength + " " +
undoState + " " + redoState + ")");
guiserver.out.flush();
lastCharCode = 65535;
}
};
undoAction = new UndoAction();
redoAction = new RedoAction();
undoableEditListener = new MyUndoableEditListener();
// set ctrl-Z and meta-Z undo/redo keys
InputMap inputMap = textPane.getInputMap();
KeyStroke key;
if(guiserver.MAC_OS_X)
key = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.META_MASK);
else
key = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK);
inputMap.put(key, undoAction);
if(guiserver.MAC_OS_X)
key = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.META_MASK | Event.SHIFT_MASK);
else
key = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK | Event.SHIFT_MASK);
inputMap.put(key, redoAction);
styledDoc.addUndoableEditListener(undoableEditListener);
styledDoc.addDocumentListener(new MyDocumentListener());
textPane.addHyperlinkListener(new Hyperactive());
textPane.addCaretListener(caretListener);
textPane.addKeyListener(keyListener);
}
public void highlightOpeningPar(char opng, char clsng)
{
int balance = -1;
String text = textPane.getText();
int currentPos = textPane.getCaretPosition();
int caretPos = 0;
int len = text.length();
caretPos = currentPos - 1; // new
// go back looking for matching parenthesis
while(balance != 0 && caretPos >= 0)
{
if(text.charAt(caretPos) == opng) ++balance;
else if(text.charAt(caretPos) == clsng) --balance;
--caretPos;
}
++caretPos;
if(balance == 0) highlightPosition(caretPos, currentPos);
}
public void highlightClosingPar(char opng, char clsng)
{
int balance = 1;
String text = textPane.getText();
int currentPos = textPane.getCaretPosition();
int caretPos = 0;
int len = text.length();
if(len == 0) return;
caretPos = currentPos;
// go forward to matching parentheseis
while(balance != 0 && caretPos < len)
{
if(text.charAt(caretPos) == opng) ++balance;
else if(text.charAt(caretPos) == clsng) --balance;
++caretPos;
}
if(balance == 0) highlightPosition(caretPos, currentPos);
}
public void highlightPosition(int pos, int oldPos)
{
textPane.setCaretPosition(pos);
textPane.getCaret().paint(textPane.getGraphics());
textPane.repaint();
try {Thread.sleep(400); } catch (InterruptedException ie) {}
textPane.setCaretPosition(oldPos);
textPane.getCaret().paint(textPane.getGraphics());
}
public void loadText(StringTokenizer tokens)
{
String path = Base64Coder.decodeString(tokens.nextToken());
EditorKit kit = textPane.getEditorKit();
System.setProperty("line.separator", "\n");
try {
if(guiserver.UTF8)
kit.read(new FilterFileReader(path, "UTF8"), styledDoc, 0);
else
kit.read(new FilterFileReader(path), styledDoc, 0);
}
catch(Exception ex) { ErrorDialog.show("gs:load-text", "Cannot load or decode file: " + path); }
}
public void saveText(StringTokenizer tokens)
{
String path = Base64Coder.decodeString(tokens.nextToken());
EditorKit kit = textPane.getEditorKit();
try { kit.write(new FileWriter(path), styledDoc, 0, styledDoc.getLength()); }
catch(Exception ex) { ErrorDialog.show("gs:save-text", "Cannot save file: " + path); }
}
public void findText(StringTokenizer tokens)
{
String findtext = tokens.nextToken();
if(guiserver.UTF8)
findtext = Base64Coder.decodeStringUTF8(findtext);
else
findtext = Base64Coder.decodeString(findtext);
String direction = "next";
boolean isRegex = false;
int dot;
String findTextAction = tokens.nextToken();
if(tokens.hasMoreTokens())
direction = tokens.nextToken();
if(tokens.hasMoreTokens())
isRegex = tokens.nextToken().equals("true");
String text = textPane.getText();
int currentCaret = textPane.getCaretPosition();
if(direction.equals("previous"))
{
if(currentCaret > findtext.length() + 1)
currentCaret -= findtext.length() + 1;
dot = text.lastIndexOf(findtext, currentCaret);
}
else
dot = text.indexOf(findtext, currentCaret);
guiserver.out.println("(" + findTextAction + " \"" + id + "\" " + dot + ")");
guiserver.out.flush();
if(dot < 0)
{
textPane.setCaretPosition(currentCaret);
return;
}
textPane.setCaretPosition(dot);
textPane.moveCaretPosition(dot + findtext.length());
}
public void setTabSize(StringTokenizer tokens)
{
int tabSize = Integer.parseInt(tokens.nextToken());
TabSet tabSet = new TabSet(new TabStop[] {
new TabStop(tabSize), new TabStop(2 * tabSize), new TabStop(3 * tabSize),
new TabStop(4 * tabSize), new TabStop(5 * tabSize), new TabStop(6 * tabSize),
new TabStop(7 * tabSize), new TabStop(8 * tabSize), new TabStop(9 * tabSize) });
SimpleAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setTabSet(attributes, tabSet);
styledDoc.setParagraphAttributes(0, documentLength, attributes, false);
}
public void setSyntax(StringTokenizer tokens)
{
String tkn = tokens.nextToken();
if(tkn.equals("true") || tkn.equals("lsp"))
syntaxSelected = SYNTAX_NEWLISP;
else if(tkn.equals("c"))
syntaxSelected = SYNTAX_C;
else if(tkn.equals("cpp"))
syntaxSelected = SYNTAX_CPP;
else if(tkn.equals("java"))
syntaxSelected = SYNTAX_JAVA;
else if(tkn.equals("php"))
syntaxSelected = SYNTAX_PHP;
else syntaxSelected = SYNTAX_NONE;
if(syntaxSelected == SYNTAX_NONE)
{
SimpleAttributeSet normal = new SimpleAttributeSet();
StyleConstants.setForeground(normal, widget.foreground);
styledDoc.setCharacterAttributes(0, documentLength, normal, true);
}
else
{
shTopLevels.removeAllElements();
if(syntaxSelected == SYNTAX_NEWLISP)
SyntaxHighlighter.color(widget, 0, documentLength);
else
{
SyntaxHighlighterC.setFlavor(syntaxSelected);
SyntaxHighlighterC.color(widget, 0, documentLength);
}
}
undo.discardAllEdits();
}
public void setForeground(StringTokenizer tokens)
{
Float red = Float.parseFloat(tokens.nextToken());
Float green = Float.parseFloat(tokens.nextToken());
Float blue = Float.parseFloat(tokens.nextToken());
if(tokens.hasMoreTokens())
{
Float alpha = Float.parseFloat(tokens.nextToken());
textPane.setForeground(new Color(red, green, blue, alpha));
}
else
textPane.setForeground(new Color(red, green, blue));
widget.foreground = textPane.getForeground();
}
class Hyperactive implements HyperlinkListener {
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
JEditorPane pane = (JEditorPane) e.getSource();
if (e instanceof HTMLFrameHyperlinkEvent) {
HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e;
HTMLDocument doc = (HTMLDocument)pane.getDocument();
doc.processHTMLFrameHyperlinkEvent(evt);
} else {
try {
pane.setPage(e.getURL());
} catch (Throwable t) {
textPane.setText("The page could not be displayed.");
}
}
}
}
}
// undo and redo
public void undoEnable(StringTokenizer tokens)
{
undoEnabled = tokens.nextToken().equals("true");
}
public void undoText(StringTokenizer tokens)
{
undoAction.actionPerformed(new ActionEvent(textPane, Event.ACTION_EVENT, "Undo"));
}
public void redoText(StringTokenizer tokens)
{
redoAction.actionPerformed(new ActionEvent(textPane, Event.ACTION_EVENT, "Redo"));
}
public void colorSyntax()
{
int caretPos = textPane.getCaretPosition();
int pos = 0;
int size = shTopLevels.size();
int p, len;
//System.out.println(".");
for(int idx = size - 1; idx >= 0; idx--)
{
pos = (Integer)shTopLevels.elementAt(idx);
if(caretPos > pos)
{
for(int i = idx; i < size; i++)
{
p = (Integer)shTopLevels.elementAt(idx);
shTopLevels.removeElementAt(idx);
}
break;
}
}
if(documentLength > caretPos + 1024)
len = caretPos + 1024;
else
len = documentLength;
if(syntaxSelected == SYNTAX_NEWLISP)
SyntaxHighlighter.color(widget, pos, len);
else
SyntaxHighlighterC.color(widget, pos, len);
}
//This one listens for edits that can be undone.
protected class MyUndoableEditListener implements UndoableEditListener
{
public void undoableEditHappened(UndoableEditEvent e)
{
//Remember the edit and update the menus.
if(SyntaxHighlighter.active || undoEnabled != true) return;
undo.addEdit(e.getEdit());
undoAction.updateUndoState();
redoAction.updateRedoState();
}
}
// Listens for any changes to the document.
protected class MyDocumentListener implements DocumentListener
{
public void insertUpdate(DocumentEvent e) { updateParams(e);}
public void removeUpdate(DocumentEvent e) { updateParams(e);}
public void changedUpdate(DocumentEvent e) { updateParams(e);}
private void updateParams(DocumentEvent e)
{
Document document = e.getDocument();
documentLength = document.getLength();
}
}
class UndoAction extends AbstractAction
{
public static final long serialVersionUID = 1L;
public UndoAction() {
super("Undo");
setEnabled(false);
}
public void actionPerformed(ActionEvent e)
{
String pname = undo.getUndoPresentationName();
try { undo.undo(); } catch (Exception ex)
{
Toolkit.getDefaultToolkit().beep();
}
String qname = undo.getUndoPresentationName();
if(pname.equals("Undo addition") && qname.equals("Undo deletion"))
{
try { undo.undo(); } catch (Exception ex)
{
Toolkit.getDefaultToolkit().beep();
}
}
updateUndoState();
redoAction.updateRedoState();
caretListener.caretUpdate(lastCaretEvent);
}
protected void updateUndoState() {
if (undo.canUndo()) {
setEnabled(true);
putValue(Action.NAME, undo.getUndoPresentationName());
}
else {
setEnabled(false);
putValue(Action.NAME, "Undo");
}
}
}
class RedoAction extends AbstractAction
{
public static final long serialVersionUID = 1L;
public RedoAction() {
super("Redo");
setEnabled(false);
}
public void actionPerformed(ActionEvent e)
{
String pname = undo.getRedoPresentationName();
try { undo.redo();} catch (Exception ex)
{
Toolkit.getDefaultToolkit().beep();
}
String qname = undo.getRedoPresentationName();
if(pname.equals("Redo deletion") && qname.equals("Redo addition"))
{
try { undo.redo(); } catch (Exception ex)
{
Toolkit.getDefaultToolkit().beep();
}
}
updateRedoState();
undoAction.updateUndoState();
caretListener.caretUpdate(lastCaretEvent);
}
protected void updateRedoState()
{
if (undo.canRedo()) {
setEnabled(true);
putValue(Action.NAME, undo.getRedoPresentationName());
}
else {
setEnabled(false);
putValue(Action.NAME, "Redo");
}
}
}
class MyCaret extends DefaultCaret
{
public static final long serialVersionUID = 1L;
public void paint(Graphics g) {
if (!isVisible()) return;
try
{
JTextComponent c = getComponent();
int dot = getDot();
Rectangle r = c.modelToView(dot);
g.setColor(c.getCaretColor());
g.fillRect(r.x, r.y, 2, r.height);
}
catch (Exception e) { System.err.println("."); }
}
protected synchronized void damage(Rectangle r)
{
if (r == null) return;
x = r.x;
y = r.y;
width = 2;
height = r.height;
repaint();
}
}
}
// eof //