package org.freeplane.plugin.script;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import jsyntaxpane.SyntaxDocument;
import jsyntaxpane.Token;
import jsyntaxpane.actions.ActionUtils;
import jsyntaxpane.components.SyntaxComponent;
import jsyntaxpane.util.Configuration;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.map.MapController;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.mode.Controller;
public class NodeIdHighLighter implements SyntaxComponent, CaretListener {
private final Pattern nodeIdPattern = Pattern.compile("(ID_\\d+)|(\"ID_\\d+\")");
private JEditorPane pane;
private Status status;
private ArrayList<NodeModel> nodesOriginallyFolded = new ArrayList<NodeModel>(50);
private NodeModel originallySelectedNode = null;
/** remove as soon as SyntaxComponent has it in the JDK5 version. */
private static enum Status {
INSTALLING,
DEINSTALLING
}
public void caretUpdate(CaretEvent e) {
handle(e.getDot());
}
public void handle(int pos) {
SyntaxDocument doc = ActionUtils.getSyntaxDocument(pane);
if (doc != null) {
try {
doc.readLock();
Token token = doc.getTokenAt(pos);
if (token == null || !handle(doc, token)) {
deHighlight();
}
}
finally {
doc.readUnlock();
}
}
}
private boolean handle(SyntaxDocument doc, Token token) {
final Matcher matcher = nodeIdPattern.matcher(token.getText(doc));
if (matcher.matches()) {
String id = matcher.group(1);
final NodeModel node = Controller.getCurrentController().getMap().getNodeForID(id);
if (node != null) {
final MapController mapController = Controller.getCurrentModeController().getMapController();
final NodeModel selectedNode = mapController.getSelectedNode();
if(node.equals(selectedNode)){
return true;
}
NodeModel originallySelectedNode = this.originallySelectedNode;
if (originallySelectedNode == null)
originallySelectedNode = mapController.getSelectedNode();
else{
deHighlight();
}
this.originallySelectedNode = originallySelectedNode;
mapController.displayNode(node, nodesOriginallyFolded);
mapController.select(node);
pane.setToolTipText(node.getText());
return true;
}
else {
pane.setToolTipText("<html><body bgcolor='#CC0000'>" //
+ TextUtils.format(getResourceKey("node_is_not_defined"), id) + "</body></html>");
}
}
else{
deHighlight();
}
return false;
}
public void deHighlight() {
if (originallySelectedNode == null)
return;
final Controller controller = Controller.getCurrentController();
if (controller == null)
return;
final MapController mapController = controller.getModeController().getMapController();
mapController.displayNode(originallySelectedNode);
mapController.select(originallySelectedNode);
foldOriginallyFolded(mapController);
originallySelectedNode = null;
pane.setToolTipText(null);
}
private void foldOriginallyFolded(final MapController mapController) {
final int countNodesOriginallyUnfolded = nodesOriginallyFolded.size();
if (countNodesOriginallyUnfolded > 0) {
for (int i = countNodesOriginallyUnfolded - 1; i >= 0; i--)
mapController.setFolded(nodesOriginallyFolded.get(i), true);
nodesOriginallyFolded.clear();
}
}
public void config(Configuration config) {
}
public void install(JEditorPane editor) {
this.pane = editor;
pane.addCaretListener(this);
handle(editor.getCaretPosition());
status = Status.INSTALLING;
addWindowListener();
// addFocusListener();
}
private void addWindowListener() {
pane.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
e.getComponent().removeFocusListener(this);
SwingUtilities.getWindowAncestor(pane).addWindowListener(new WindowAdapter(){
@Override
public void windowClosed(WindowEvent e) {
e.getWindow().removeWindowListener(this);
deHighlight();
}
});
}
});
}
// private void addFocusListener() {
// class NodeIdHighLightFocusListener extends FocusAdapter {
// public void focusLost(FocusEvent e) {
// deHighlight();
// }
// }
// final FocusListener[] focusListeners = pane.getFocusListeners();
// for (int i = 0; i < focusListeners.length; i++) {
// if (focusListeners[i] instanceof NodeIdHighLightFocusListener) {
// return;
// }
// }
// pane.addFocusListener(new NodeIdHighLightFocusListener());
// }
public void deinstall(JEditorPane editor) {
status = Status.DEINSTALLING;
deHighlight();
pane.removeCaretListener(this);
}
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("document")) {
pane.removeCaretListener(this);
if (status.equals(Status.INSTALLING)) {
pane.addCaretListener(this);
deHighlight();
}
}
}
public String getResourceKey(final String key) {
return "org.freeplane.plugin.script.NodeIdHighlighter." + key;
}
}