/*
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 java.awt.Dimension;
import java.awt.Font;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import javax.swing.JEditorPane;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import org.geogebra.desktop.gui.GeoGebraKeys;
import org.geogebra.desktop.main.AppD;
/**
*
* @author Calixte DENIZET
*
*/
public class GeoGebraEditorPane extends JEditorPane implements CaretListener,
MouseListener, MouseMotionListener, FocusListener {
private static final long serialVersionUID = 1L;
private static final int GEOGEBRA = 0;
private static final int LATEX = 1;
private static final int JAVASCRIPT = 2;
private Popup helpPopup;
private AppD app;
private int rows;
private int cols;
private int rowHeight;
private int columnWidth;
private Lexer lexer;
private boolean matchingEnable;
private MatchingBlockManager matchLR;
private MatchingBlockManager matchRL;
private Point mousePoint;
private List<KeywordListener> kwListeners = new ArrayList<KeywordListener>();
private int type;
/**
* Default Constructor
*
* @param app
* app
*
* @param rows
* the number of rows to use
* @param cols
* the number of columns to use
*/
public GeoGebraEditorPane(AppD app, int rows, int cols) {
super();
this.app = app;
this.rows = rows;
this.cols = cols;
// TODO temporarily remove the caretListener
// It causes a np exception with the PropertiesView.... why?
// addCaretListener(this);
addMouseMotionListener(this);
addMouseListener(this);
addFocusListener(this);
addKeyListener(new GeoGebraKeys());
}
/**
* @param kitString
*/
public void setEditorKit(String kitString) {
String str = getText();
if ("geogebra".equalsIgnoreCase(kitString)) {
GeoGebraEditorKit ggbKit = new GeoGebraEditorKit(app);
super.setEditorKit(ggbKit);
setFont(ggbKit.getStylePreferences().tokenFont);
lexer = new GeoGebraLexer(getDocument(), app);
type = GEOGEBRA;
} else if ("latex".equalsIgnoreCase(kitString)) {
LaTeXEditorKit ltxKit = new LaTeXEditorKit(app);
super.setEditorKit(ltxKit);
setFont(ltxKit.getStylePreferences().tokenFont);
lexer = new LaTeXLexer(getDocument());
type = LATEX;
} else if ("javascript".equalsIgnoreCase(kitString)) {
JavascriptEditorKit javascriptKit = new JavascriptEditorKit(app);
super.setEditorKit(javascriptKit);
setFont(javascriptKit.getStylePreferences().tokenFont);
lexer = new JavascriptLexer(getDocument());
((JavascriptEditorKit.JavascriptDocument) getDocument())
.setTextComponent(this);
type = JAVASCRIPT;
}
matchLR = new MatchingBlockManager(getDocument(), this, true,
getHighlighter());
matchLR.setDefaults();
matchRL = new MatchingBlockManager(getDocument(), this, false,
getHighlighter());
matchRL.setDefaults();
enableMatchingKeywords(true);
setText(str);
}
/**
* {@inheritDoc}
*/
@Override
public void setFont(Font f) {
super.setFont(f);
this.columnWidth = getFontMetrics(f).charWidth('m');
this.rowHeight = getFontMetrics(getFont()).getHeight();
}
public void updateFont(Font f) {
((ViewContext) getEditorKit().getViewFactory()).setTokenFont(f);
super.setFont(f);
revalidate();
}
// =================================================================
//
// TODO These two methods are preventing the script editor from scrolling.
// Do we
// need them, can they be removed?
/**
* {@inheritDoc}
*/
/*
* @Override public Dimension getPreferredSize() { Dimension dim =
* super.getPreferredSize(); dim = (dim == null) ? new Dimension(400, 400) :
* dim; if (cols != 0) { dim.width = cols * columnWidth; } if (rows != 0) {
* dim.height = rows * rowHeight; }
*
* return dim; }
*/
/**
* {@inheritDoc}
*/
/*
* @Override public Dimension getPreferredScrollableViewportSize() { return
* new Dimension(cols * columnWidth, rows * rowHeight); }
*/
// =================================================================
/**
* Returns preferred dimension for the given number of rows and columns when
* using the current font.
*
* @param row
* @param column
* @return
*/
public Dimension getPreferredSizeFromRowColumn(int row, int column) {
Dimension dim = new Dimension(400, 400);
if (column != 0) {
dim.width = cols * columnWidth;
}
if (row != 0) {
dim.height = rows * rowHeight;
}
return dim;
}
/**
* Add a new KeywordListener
*
* @param kw
* a KeywordListener
*/
public void addKeywordListener(KeywordListener kw) {
if (!kwListeners.contains(kw)) {
kwListeners.add(kw);
}
}
/**
* Remove a new KeywordListener
*
* @param kw
* a KeywordListener
*/
public void removeKeywordListener(KeywordListener kw) {
if (kwListeners.contains(kw)) {
kwListeners.remove(kw);
}
}
/**
* @return an array of KeywordListener
*/
public KeywordListener[] getKeywordListeners() {
return kwListeners.toArray(new KeywordListener[0]);
}
/**
* Enable (active true) or disable (active false) the matching keywords.
*
* @param active
* true or false
*/
public void enableMatchingKeywords(boolean active) {
matchingEnable = active;
}
/**
* Get a matching manager
*
* @param lr
* true if the LR matcher must be returned
* @return the MatchingBlockManager
*/
public MatchingBlockManager getMatchingBlockManager(boolean lr) {
if (lr) {
return matchLR;
}
return matchRL;
}
/**
* This class listens to the caret event
*
* @param e
* event
*/
@Override
public void caretUpdate(CaretEvent e) {
if (lexer != null) {
int pos = getCaretPosition();
int ltok = lexer.getKeyword(pos, false);
int start = lexer.start + lexer.yychar();
int rtok = lexer.getKeyword(pos, true);
if (matchingEnable) {
matchLR.searchMatchingBlock(ltok, start);
matchRL.searchMatchingBlock(rtok,
lexer.start + lexer.yychar() + lexer.yylength());
}
if (type == GEOGEBRA && rtok == GeoGebraLexerConstants.OPENCLOSE) {
pos = getCaretPosition() - 1;
rtok = lexer.getKeyword(pos, true);
if (rtok == GeoGebraLexerConstants.COMMAND) {
try {
HelpOnKeywordPanel panel = HelpOnKeywordPanel
.getInstance(app,
getDocument().getText(
lexer.start + lexer.yychar(),
lexer.yylength()));
Point p = this.getLocationOnScreen();
Rectangle r = modelToView(pos);
if (helpPopup != null) {
helpPopup.hide();
}
helpPopup = PopupFactory.getSharedInstance().getPopup(
this, panel, p.x + r.x,
p.y + r.y + 2 + r.height);
helpPopup.show();
} catch (BadLocationException ex) {
//
} catch (IllegalComponentStateException ex) {
//
}
}
} else {
if (helpPopup != null) {
helpPopup.hide();
helpPopup = null;
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void focusGained(FocusEvent e) {
//
}
/**
* {@inheritDoc}
*/
@Override
public void focusLost(FocusEvent e) {
if (helpPopup != null) {
helpPopup.hide();
helpPopup = null;
}
}
/**
* Get a keyword at a position in the document.
*
* @param position
* in the document
* @return the KeywordEvent containing infos about keyword.
*/
public KeywordEvent getKeywordEvent(int position) {
int tok = lexer.getKeyword(position, true);
return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(),
lexer.yylength());
}
/**
* Get a keyword at the current position in the document.
*
* @return the KeywordEvent containing infos about keyword.
*/
public KeywordEvent getKeywordEvent() {
return getKeywordEvent(getCaretPosition());
}
/**
* Get a keyword at the current position in the document.
*
* @param caret
* if true the position is the current caret position in the doc
* else the position is the mouse pointer position projected in
* the document.
* @param strict
* if true the char just after the caret is ignored
* @return the KeywordEvent containing infos about keyword.
*/
public KeywordEvent getKeywordEvent(boolean caret, boolean strict) {
int tok;
if (caret) {
tok = lexer.getKeyword(getCaretPosition(), strict);
} else {
tok = lexer.getKeyword(viewToModel(mousePoint), strict);
}
return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(),
lexer.yylength());
}
/**
* Prevents the different KeywordListener that a MouseEvent occured
*
* @param position
* of the mouse
* @param ev
* the event which occured
* @param type1
* of the event : KeywordListener.ONMOUSECLICKED or
* KeywordListener.ONMOUSEOVER
*/
protected void preventConcernedKeywordListener(int position, EventObject ev,
int type1) {
int tok = lexer.getKeyword(position, true);
KeywordEvent kev = new KeywordEvent(this, ev, tok,
lexer.start + lexer.yychar(), lexer.yylength());
for (KeywordListener listener : kwListeners) {
if (type1 == listener.getType()) {
listener.caughtKeyword(kev);
}
}
}
/**
* Implements mouseClicked in MouseListener
*
* @param e
* event
*/
@Override
public void mouseClicked(MouseEvent e) {
preventConcernedKeywordListener(getCaretPosition(), e,
KeywordListener.ONMOUSECLICKED);
}
/**
* Implements mouseEntered in MouseListener
*
* @param e
* event
*/
@Override
public void mouseEntered(MouseEvent e) {
this.mousePoint = e.getPoint();
}
/**
* Implements mouseExited in MouseListener
*
* @param e
* event
*/
@Override
public void mouseExited(MouseEvent e) {
//
}
/**
* Implements mousePressed in MouseListener
*
* @param e
* event
*/
@Override
public void mousePressed(MouseEvent e) {
//
}
/**
* Implements mouseReleseaed in MouseListener
*
* @param e
* event
*/
@Override
public void mouseReleased(MouseEvent e) {
//
}
/**
* Implements mouseMoved in MouseMotionListener
*
* @param e
* event
*/
@Override
public void mouseMoved(MouseEvent e) {
this.mousePoint = e.getPoint();
preventConcernedKeywordListener(viewToModel(mousePoint), e,
KeywordListener.ONMOUSEOVER);
}
/**
* Implements mouseDragged in MouseMotionListener
*
* @param e
* event
*/
@Override
public void mouseDragged(MouseEvent e) {
//
}
/**
* @return the current mouse position in this pane
*/
public Point getMousePoint() {
return mousePoint;
}
}