/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This code has been written initially for Scilab (http://www.scilab.org/).
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.
*/
package org.geogebra.desktop.gui.editor;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.PlainDocument;
import javax.swing.text.ViewFactory;
import org.geogebra.desktop.main.AppD;
/**
*
* @author Calixte DENIZET
*
*/
public class JavascriptEditorKit extends DefaultEditorKit {
private static final long serialVersionUID = 1L;
/**
* The mimetype for a GeoGebra code
*/
public static final String MIMETYPE = "text/javascript";
private static final String COMMENTLINE = "commentline";
private static final String COMMENTMULTILINE = "commentmultiline";
private JavascriptContext preferences;
private AppD app;
/**
*
* @param app
* the Application where this kit is used
*/
public JavascriptEditorKit(AppD app) {
this.app = app;
}
/**
* {@inheritDoc}
*/
@Override
public String getContentType() {
return MIMETYPE;
}
/**
* @return the context associated with the ScilabDocument
*/
public JavascriptContext getStylePreferences() {
if (preferences == null) {
preferences = new JavascriptContext(app);
}
return preferences;
}
/**
* {@inheritDoc}
*/
@Override
public Document createDefaultDocument() {
return new JavascriptDocument();
}
/**
* {@inheritDoc}
*/
@Override
public ViewFactory getViewFactory() {
return getStylePreferences();
}
/**
* Inner class to handle a Javascript document where several consecutive
* lines can be commented.
*
* @author Calixte DENIZET
*
*/
public static class JavascriptDocument extends PlainDocument
implements DocumentListener {
private static final long serialVersionUID = 1L;
private GeoGebraEditorPane textcomponent;
/**
* Default constructor
*/
public JavascriptDocument() {
super();
addDocumentListener(this);
}
/**
* @param el
* the Element to test
* @return true if the line is commented part of a multi line comment
*/
public boolean isMultiLineCommented(Element el) {
return ((MutableAttributeSet) el.getAttributes())
.containsAttribute(COMMENTMULTILINE, COMMENTMULTILINE);
}
/**
* @param el
* the Element to test
* @return true if the line is a single line comment
*/
public boolean isLineCommented(Element el) {
return ((MutableAttributeSet) el.getAttributes())
.containsAttribute(COMMENTLINE, COMMENTLINE);
}
/**
* @param el
* the index of Element to test
* @return true if the line is commented part of a multi line comment
*/
public boolean isMultiLineCommented(int index) {
Element root = getDefaultRootElement();
if (index < 0 || index >= root.getElementCount()) {
return false;
}
return isMultiLineCommented(root.getElement(index));
}
/**
* @param index
* the index of Element to test
* @return true if the line is a single line comment
*/
public boolean isLineCommented(int index) {
Element root = getDefaultRootElement();
if (index < 0 || index >= root.getElementCount()) {
return false;
}
return isLineCommented(root.getElement(index));
}
public void setTextComponent(GeoGebraEditorPane pane) {
this.textcomponent = pane;
}
/**
* Test if an element is commented
*
* @param el
* element of the line to test
* @return true if the line is commented
*/
public boolean isCommented(Element el) {
return isMultiLineCommented(el) || isLineCommented(el);
}
/**
* Test if a line is commented
*
* @param index
* of the line to test
* @return true if the line is commented
*/
public boolean isCommented(int index) {
Element root = getDefaultRootElement();
if (index < 0 || index >= root.getElementCount()) {
return false;
}
return isMultiLineCommented(root.getElement(index))
|| isLineCommented(root.getElement(index));
}
/**
* {@inheritDoc}
*/
@Override
public void insertUpdate(DocumentEvent ev) {
handleUpdate(ev);
}
/**
* {@inheritDoc}
*/
@Override
public void removeUpdate(DocumentEvent ev) {
handleUpdate(ev);
}
/**
* {@inheritDoc}
*/
@Override
public void changedUpdate(DocumentEvent e) {
// only handle insert and delete
}
/**
* Handle the remove or insert events.
*
* @param ev
* the event
*/
protected void handleUpdate(DocumentEvent ev) {
Element root = getDefaultRootElement();
DocumentEvent.ElementChange ec = ev.getChange(root);
if (ec != null) {
Element[] added = ec.getChildrenAdded();
boolean comment = isCommented(ec.getIndex() - 1);
for (int i = 0; i < added.length; i++) {
comment = handleElement(added[i], comment);
}
Element[] removed = ec.getChildrenRemoved();
comment = isCommented(ec.getIndex() - 1);
for (int i = 0; i < removed.length; i++) {
comment = handleElement(removed[i], comment);
}
} else {// the event occured in one line
int index = root.getElementIndex(ev.getOffset());
Element elem = root.getElement(index);
boolean com = isCommented(elem);
if (handleElement(elem, isCommented(index - 1)) != com) {
boolean comment = false;
for (int i = 0; i < root.getElementCount(); i++) {
comment = handleElement(root.getElement(i), comment);
}
if (textcomponent != null) {
textcomponent.repaint();
}
}
}
}
/**
* @param elem
* the element to handle
* @param previous
* true if the previous line is commented
* @return true if this line is commented
*/
private boolean handleElement(Element elem, boolean previous) {
int p0 = elem.getStartOffset();
int p1 = elem.getEndOffset();
boolean comment = false;
String s;
int tok = textcomponent.getKeywordEvent(p1 - 1).getType();
try {
s = getText(p0, p1 - p0);
} catch (BadLocationException e) {
s = "";
}
if (tok == JavascriptLexerConstants.LINECOMMENTS) {
comment = true;
((MutableAttributeSet) elem.getAttributes())
.addAttribute(COMMENTLINE, COMMENTLINE);
} else if (tok == JavascriptLexerConstants.MULTILINECOMMENTS
&& !s.endsWith("*/\n")) {
comment = true;
((MutableAttributeSet) elem.getAttributes())
.addAttribute(COMMENTMULTILINE, COMMENTMULTILINE);
} else {
((MutableAttributeSet) elem.getAttributes())
.removeAttribute(COMMENTLINE);
((MutableAttributeSet) elem.getAttributes())
.removeAttribute(COMMENTMULTILINE);
}
return comment;
}
}
}