package org.eclipse.iee.pad.formula.ui;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.util.Collection;
import org.eclipse.draw2d.AncestorListener;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.ImageFigure;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.ToolbarLayout;
import org.eclipse.draw2d.text.FlowPage;
import org.eclipse.iee.core.document.PadDocumentPart;
import org.eclipse.iee.core.document.text.Document;
import org.eclipse.iee.core.document.text.EventBusSupport;
import org.eclipse.iee.core.document.text.INode;
import org.eclipse.iee.core.document.text.INodeVisitor;
import org.eclipse.iee.core.document.text.ISpanContent;
import org.eclipse.iee.core.document.text.Span;
import org.eclipse.iee.core.document.text.Text;
import org.eclipse.iee.editor.core.bindings.DefaultObservableValue;
import org.eclipse.iee.editor.core.bindings.IObservableValue;
import org.eclipse.iee.editor.core.container.TextRenderCtx;
import org.eclipse.iee.editor.core.pad.FigurePad;
import org.eclipse.iee.editor.core.pad.Pad;
import org.eclipse.iee.editor.core.pad.common.text.IEditorLocation;
import org.eclipse.iee.editor.core.pad.common.ui.IMenuContributor;
import org.eclipse.iee.editor.core.utils.runtime.file.FileMessageEvent;
import org.eclipse.iee.editor.core.utils.runtime.file.FileMessager;
import org.eclipse.iee.editor.core.utils.runtime.file.IFileMessageListener;
import org.eclipse.iee.pad.formula.ui.utils.Function;
import org.eclipse.iee.pad.formula.ui.utils.UIFormulaRenderer;
import org.eclipse.iee.pad.text.ui.DocumentEditor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import com.google.common.base.Optional;
import com.google.common.eventbus.Subscribe;
public abstract class AbstractFormulaPad<T extends PadDocumentPart> extends FigurePad<T, Figure> implements IMenuContributor {
private UIFormulaRenderer fFormulaRenderer;
protected String fResult;
private ImageFigure fHoverShell;
protected boolean fIsInputValid;
protected String fOriginalExpression = "";
protected String fTranslatingExpression = "";
protected String fLastValidText = "";
protected String fTexExpression = "";
private final Color INPUT_VALID_COLOR = new Color(null, 255, 255, 255);
private final Color INPUT_INVALID_COLOR = new Color(null, 255, 0, 0);
private IFileMessageListener fFileMessageListener = new IFileMessageListener() {
@Override
public void messageReceived(FileMessageEvent e) {
updateLastResult(e.getMessage());
}
@Override
public String getRequesterID() {
return getContainerID();
}
};
private TextRenderCtx fRenderCtx;
private DocumentEditor fDocumentEditor;
private ImageFigure fFormulaFigure = new ImageFigure();
private ImageFigure fResultFigure = new ImageFigure();
private AncestorListener.Stub fAncestorListener;
public AbstractFormulaPad(UIFormulaRenderer formulaRenderer, TextRenderCtx renderCtx) {
fFormulaRenderer = formulaRenderer;
fRenderCtx = renderCtx;
fDocumentEditor = new DocumentEditor(renderCtx);
addEditor(fDocumentEditor);
EventBusSupport.getDefault().register(this);
fAncestorListener = new AncestorListener.Stub() {
@Override
public void ancestorMoved(IFigure ancestor) {
updateHover();
}
};
}
@Subscribe
public void onPropertyChange(PropertyChangeEvent event) {
Object source = event.getSource();
Document document = fDocumentEditor.getModel();
if (document != null && (source instanceof INode) && ((INode)source).hasParent(document)) {
fTexExpression = getText();
updateHover();
}
}
private void updateHover() {
validateInput();
removeFormulaHover();
Image image = createImage(fTexExpression);
if (image == null) {
fTexExpression = fLastValidText;
image = createImage(fTexExpression);
}
fHoverShell = new ImageFigure(image);
fHoverShell.setBorder(new LineBorder(1));
// hack to paint hover image after widgets size
// recalculation.
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
if (fHoverShell != null) {
Rectangle bounds = getBounds();
getContainer().getFeedbackFigure().add(
fHoverShell,
new org.eclipse.draw2d.geometry.Rectangle(bounds.x + 5, bounds.y + bounds.height + 5, -1, -1)
);
}
}
});
}
protected String getText() {
Document document = fDocumentEditor.getModel();
String text = document.accept(new INodeVisitor<TextCtx, CharSequence>() {
@Override
public CharSequence visitText(Text text, TextCtx ctx) {
return text.getText();
}
@Override
public CharSequence visitDocument(Document document, TextCtx ctx) {
StringBuilder sb = new StringBuilder();
for (INode content : document.getChildren()) {
sb.append(content.accept(this, ctx));
}
return sb;
}
@Override
public CharSequence visitSpan(Span span, TextCtx ctx) {
StringBuilder sb = new StringBuilder();
for (ISpanContent content : span.getChildren()) {
sb.append(content.accept(this, ctx));
}
return sb;
}}, new TextCtx()).toString();
return text;
}
public String getOriginalExpression() {
return fOriginalExpression;
}
public void setOriginalExpression(String expression) {
fOriginalExpression = expression;
}
public String getTranslatingExpression() {
return fTranslatingExpression;
}
public void setTranslatingExpression(String expression) {
fTranslatingExpression = expression;
Document document = new Document();
Span child = new Span();
Text text = new Text();
text.setText(expression);
child.addChild(text);
document.addChild(child);
Optional<? extends IObservableValue<Document>> of = Optional.of(DefaultObservableValue.fromValue(document));
fDocumentEditor.setValue(of);
updateFormulaImage();
}
public UIFormulaRenderer getFormulaRenderer() {
return fFormulaRenderer;
}
public void asyncUIUpdate(final Function function) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
function.f();
}
});
}
public void toggleInputText() {
Figure figure = getFigure();
figure.removeAll();
FlowPage editorFigure = fDocumentEditor.getFigure();
editorFigure.addAncestorListener(fAncestorListener);
figure.add(editorFigure);
updateHover();
}
public void toggleFormulaImage() {
Figure figure = getFigure();
FlowPage editorFigure = fDocumentEditor.getFigure();
editorFigure.removeAncestorListener(fAncestorListener);
toggleFormulaFigure(figure);
}
private void toggleFormulaFigure(Figure figure) {
figure.removeAll();
figure.setLayoutManager(new ToolbarLayout(true));
figure.add(fFormulaFigure);
figure.add(fResultFigure);
}
public void setInputIsValid() {
fIsInputValid = true;
// fViewer.getControl().setBackground(INPUT_VALID_COLOR);
}
public void setInputIsInvalid() {
fIsInputValid = false;
// fViewer.getControl().setBackground(INPUT_INVALID_COLOR);
}
public void validateInput() {
// TODO: add validation
String text = getText();
fOriginalExpression = text;
setInputIsValid();
fLastValidText = text;
}
public void processInput() {
if (fIsInputValid) {
if (!getText().equals(fTranslatingExpression)) {
/* Remove result images from following pads */
Collection<Pad<?, ?>> following = FormulaPadManager
.getFollowingPads(this);
for (Pad<?, ?> pad : following) {
((AbstractFormulaPad<?>) pad).updateLastResult("");
}
}
}
fTranslatingExpression = fLastValidText;
updateFormulaImage();
}
private void updateFormulaImage() {
fTexExpression = fTranslatingExpression;
Image image = fFormulaRenderer.getFormulaImage(fTexExpression);
updateFormulaImage(image);
}
protected void updateFormulaImage(Image image) {
fFormulaFigure.setImage(image);
}
public void updateLastResult(String result) {
final Image image;
fResult = result;
if (result == "")
image = null;
else {
String latex = result;
fTexExpression += latex;
image = fFormulaRenderer.getFormulaImage(latex);
}
Function updateImage = new Function() {
@Override
public void f() {
updateLastResult(image);
}
};
asyncUIUpdate(updateImage);
}
protected void updateLastResult(Image image) {
fResultFigure.setImage(image);
}
private void switchToResultView() {
processInput();
focusOnMainEditor();
toggleFormulaImage();
removeFormulaHover();
}
private void removeFormulaHover() {
if (fHoverShell != null) {
if (fHoverShell.getParent() != null) {
fHoverShell.getParent().remove(fHoverShell);
}
fHoverShell = null;
}
}
public void setListeners() {
FileMessager.getInstance().addFileMessageListener(fFileMessageListener,
getContainer().getContainerManager().getStoragePath());
// fViewer.getTextWidget().addMouseWheelListener(new MouseWheelListener() {
//
// @Override
// public void mouseScrolled(MouseEvent e) {
// removeFormulaHover();
// }
// });
}
protected Image createImage(String formula) {
return fFormulaRenderer.getFormulaImage(fTexExpression);
}
@Override
protected Figure createFigure() {
Figure figure = new Figure();
figure.setLayoutManager(new ToolbarLayout());
validateInput();
updateFormulaImage();
toggleFormulaFigure(figure);
return figure;
}
@Override
public void contribute(MenuManager menuManager) {
Action action = new Action("Copy result", SWT.PUSH) {
@Override
public void run() {
Clipboard clipboard = new Clipboard(Display.getCurrent());
try {
TextTransfer transfer = TextTransfer.getInstance();
clipboard.setContents(new Object[] { fResult },
new Transfer[] { transfer });
} finally {
clipboard.dispose();
}
}
};
menuManager.add(action);
}
@Override
public void activate() {
toggleInputText();
}
@Override
public void deactivate() {
switchToResultView();
}
public void save() {
processInput();
}
@Override
public String getType() {
return "Formula";
}
@Override
public String getTex() {
return fTexExpression;
}
@Override
public boolean handleKey(KeyEvent e) {
switch (e.keyCode) {
case SWT.CR:
switchToResultView();
moveCaretToContainerTail();
return true;
case SWT.ESC:
switchToResultView();
moveCaretToContainerTail();
return true;
}
return super.handleKey(e);
}
@Override
public Optional<IEditorLocation> getTextLocation(int x, int y) {
return fDocumentEditor.getStart();
}
}