/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.components.widget;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openflexo.FlexoCst;
import org.openflexo.foundation.dm.DuplicateMethodSignatureException;
import org.openflexo.foundation.dm.javaparser.AbstractSourceCode;
import org.openflexo.foundation.dm.javaparser.ParserNotInstalledException;
import org.openflexo.inspector.AbstractController;
import org.openflexo.inspector.InspectableObject;
import org.openflexo.inspector.model.PropertyModel;
import org.openflexo.inspector.widget.WidgetFocusListener;
import org.openflexo.jedit.InputHandler.InputListener;
import org.openflexo.jedit.cd.JavaCodeDisplayer;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.swing.diff.DiffPanel;
import org.openflexo.toolbox.TokenMarkerStyle;
/**
* This widget may be used to represent source code that can be edited (a diff-view shows differences between edited code and default
* computed code) Parsing errors are also shown in this widget This widget requires a
*
* <pre>
* AbstractSourceCode
* </pre>
*
* object.
*
* @author sylvain
*
*/
public class JavaSourceCodeInspectorWidget extends CustomInspectorWidget<AbstractSourceCode> {
private static final Logger logger = FlexoLogger.getLogger(JavaSourceCodeInspectorWidget.class.getPackage().getName());
public static final String COLUMNS = "columns";
public static final String ROWS = "rows";
public static final String VALIDATE_ON_RETURN = "validateOnReturn";
private static final int DEFAULT_COLUMNS = 10;
private static final int DEFAULT_ROWS = 4;
public static Font FIXED_SIZE_FONT = new Font("Monospaced", Font.PLAIN, 11);
public static Font SANS_SERIF_FONT = new Font("SansSerif", Font.PLAIN, 11);
protected class SourceCodePanel extends JPanel {
protected JPanel top;
protected JLabel statusLabel;
protected JLabel errorLabel;
protected JLabel resetImplementationLabel;
protected JLabel editCodeLabel;
protected JLabel showDiffLabel;
protected JavaCodeDisplayer _textArea;
protected boolean showDiffMode = false;
protected boolean diffPanelDisplayed = false;
protected boolean validateOnReturn;
/**
*
*/
public SourceCodePanel(PropertyModel model) {
super(new BorderLayout());
setOpaque(false);
_textArea = new JavaCodeDisplayer(getStringValue());
_textArea.setEditable(true);
if (model.hasValueForParameter(VALIDATE_ON_RETURN)) {
validateOnReturn = model.getBooleanValueForParameter(VALIDATE_ON_RETURN);
} else {
validateOnReturn = true;
}
if (model.hasValueForParameter(COLUMNS)) {
_textArea.setColumns(model.getIntValueForParameter(COLUMNS));
} else {
_textArea.setColumns(DEFAULT_COLUMNS);
}
if (model.hasValueForParameter(ROWS)) {
_textArea.setRows(model.getIntValueForParameter(ROWS));
} else {
_textArea.setRows(DEFAULT_ROWS);
}
if (model.hasValueForParameter("font")) {
if (model.getValueForParameter("font").equals("SansSerif")) {
_textArea.setFont(SANS_SERIF_FONT);
} else if (model.getValueForParameter("font").equals("FixedSize")) {
_textArea.setFont(FIXED_SIZE_FONT);
}
}
_textArea.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
if (!validateOnReturn && !widgetUpdating) {
updateModelFromWidget();
}
}
@Override
public void insertUpdate(DocumentEvent e) {
if (!validateOnReturn && !widgetUpdating) {
updateModelFromWidget();
}
}
@Override
public void removeUpdate(DocumentEvent e) {
if (!validateOnReturn && !widgetUpdating) {
updateModelFromWidget();
}
}
});
_textArea.getInputHandler().addToInputListener(new InputListener() {
@Override
public void enterPressed(KeyEvent event) {
updateModelFromWidget();
}
});
_textArea.getPainter().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
updateModelFromWidget();
}
});
_textArea.addFocusListener(new WidgetFocusListener(JavaSourceCodeInspectorWidget.this));
// _textArea.setAutoscrolls(true);
_textArea.setBorder(BorderFactory.createLoweredBevelBorder());
_textArea.setEnabled(true);
top = new JPanel(new BorderLayout());
top.setOpaque(false);
statusLabel = new JLabel("status", SwingConstants.LEFT);
statusLabel.setFont(FlexoCst.SMALL_FONT);
resetImplementationLabel = new JLabel("<html><u>" + FlexoLocalization.localizedForKey("reset_to_default_implementation")
+ "</u>" + "</html>", SwingConstants.RIGHT);
resetImplementationLabel.setForeground(Color.BLUE);
resetImplementationLabel.setFont(FlexoCst.SMALL_FONT);
resetImplementationLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
resetImplementationLabel.setForeground(Color.MAGENTA);
resetImplementationLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
@Override
public void mouseExited(MouseEvent e) {
resetImplementationLabel.setForeground(Color.BLUE);
resetImplementationLabel.setCursor(Cursor.getDefaultCursor());
}
@Override
public void mouseClicked(MouseEvent e) {
editionRequested = false;
showDiffMode = false;
try {
getSourceCode().setCode("");
} catch (ParserNotInstalledException e1) {
e1.printStackTrace();
} catch (DuplicateMethodSignatureException e1) {
e1.printStackTrace();
}
refreshPanel();
}
});
editCodeLabel = new JLabel("<html><u>" + FlexoLocalization.localizedForKey("edit_code") + "</u>" + "</html>",
SwingConstants.RIGHT);
editCodeLabel.setForeground(Color.BLUE);
editCodeLabel.setFont(FlexoCst.SMALL_FONT);
editCodeLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
editCodeLabel.setForeground(Color.MAGENTA);
editCodeLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
@Override
public void mouseExited(MouseEvent e) {
editCodeLabel.setForeground(Color.BLUE);
editCodeLabel.setCursor(Cursor.getDefaultCursor());
}
@Override
public void mouseClicked(MouseEvent e) {
editionRequested = true;
showDiffMode = false;
refreshPanel();
}
});
showDiffLabel = new JLabel("<html><u>" + FlexoLocalization.localizedForKey("show_diffs") + "</u>" + "</html>",
SwingConstants.RIGHT);
showDiffLabel.setForeground(Color.BLUE);
showDiffLabel.setFont(FlexoCst.SMALL_FONT);
showDiffLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
showDiffLabel.setForeground(Color.MAGENTA);
showDiffLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
@Override
public void mouseExited(MouseEvent e) {
showDiffLabel.setForeground(Color.BLUE);
showDiffLabel.setCursor(Cursor.getDefaultCursor());
}
@Override
public void mouseClicked(MouseEvent e) {
updateModelFromWidget();
showDiffMode = true;
editionRequested = false;
refreshPanel();
}
});
top.add(statusLabel, BorderLayout.WEST);
JPanel actionsPanel = new JPanel(new FlowLayout());
actionsPanel.add(showDiffLabel);
actionsPanel.add(editCodeLabel);
actionsPanel.add(resetImplementationLabel);
top.add(actionsPanel, BorderLayout.EAST);
errorLabel = new JLabel("error", SwingConstants.CENTER);
add(top, BorderLayout.NORTH);
add(_textArea, BorderLayout.CENTER);
add(errorLabel, BorderLayout.SOUTH);
editionRequested = true;
showDiffMode = false;
refreshPanel();
validate();
doLayout();
}
protected boolean editionRequested = false;
public void updateModelFromWidget() {
if (isUpdatingWidget || isUpdatingModel || getSourceCode() == null || !(getSourceCode().isEdited() || editionRequested)) {
return;
}
isUpdatingModel = true;
try {
getSourceCode().setCode(_textArea.getText());
refreshPanel();
} catch (ParserNotInstalledException e) {
e.printStackTrace();
} catch (DuplicateMethodSignatureException e) {
e.printStackTrace();
} finally {
isUpdatingModel = false;
}
}
private DiffPanel diffPanel;
private void switchToDiffMode() {
diffPanel = new DiffPanel(getSourceCode().getDiffReport(), TokenMarkerStyle.Java,
FlexoLocalization.localizedForKey("edited_code"), FlexoLocalization.localizedForKey("default_implementation"),
FlexoLocalization.localizedForKey("no_differences"), true);
remove(_textArea);
add(diffPanel, BorderLayout.CENTER);
diffPanelDisplayed = true;
}
private void switchToNormalMode() {
remove(diffPanel);
diffPanel = null;
add(_textArea, BorderLayout.CENTER);
diffPanelDisplayed = false;
}
private void resetToDefault() {
if (showDiffMode) {
switchToNormalMode();
}
showDiffMode = false;
editionRequested = false;
}
protected void refreshPanel() {
if (getSourceCode() == null) {
return;
}
boolean codeModified = !_textArea.getText().equals(getSourceCode().getCode());
int caretPos = 0;
if (showDiffMode && !diffPanelDisplayed) {
switchToDiffMode();
} else if (!showDiffMode && diffPanelDisplayed) {
switchToNormalMode();
}
if (codeModified) {
caretPos = _textArea.getCaretPosition();
_textArea.setText(getSourceCode().getCode());
}
if (!showDiffMode) {
if (getSourceCode().isEdited() || editionRequested) {
statusLabel.setText("[" + FlexoLocalization.localizedForKey("edited_code") + "]");
if (!_textArea.isEditable()) {
_textArea.setEditable(true);
}
resetImplementationLabel.setVisible(true);
showDiffLabel.setVisible(true);
editCodeLabel.setVisible(false);
} else {
statusLabel.setText("[" + FlexoLocalization.localizedForKey("default_implementation") + "]");
if (_textArea.isEditable()) {
_textArea.setEditable(false);
}
resetImplementationLabel.setVisible(false);
showDiffLabel.setVisible(false);
editCodeLabel.setVisible(true);
}
} else {
resetImplementationLabel.setVisible(true);
showDiffLabel.setVisible(false);
editCodeLabel.setVisible(true);
}
if (getSourceCode().hasParseErrors()) {
errorLabel.setText(getSourceCode().getParseErrorWarning());
errorLabel.setVisible(true);
} else {
errorLabel.setVisible(false);
}
if (codeModified) {
_textArea.setCaretPosition(caretPos);
}
revalidate();
doLayout();
}
public void updateWidgetFromModel() {
if (isUpdatingModel || getSourceCode() == null) {
return;
}
isUpdatingWidget = true;
try {
refreshPanel();
} finally {
isUpdatingWidget = false;
}
}
}
private SourceCodePanel panel;
protected boolean isUpdatingModel = false;
protected boolean isUpdatingWidget = false;
/**
*
*
*/
public AbstractSourceCode getSourceCode() {
return getObjectValue();
}
/**
* @param model
*/
public JavaSourceCodeInspectorWidget(PropertyModel model, AbstractController controller) {
super(model, controller);
panel = new SourceCodePanel(model);
}
/**
* Overrides getDefaultType
*
* @see org.openflexo.inspector.widget.DenaliWidget#getDefaultType()
*/
@Override
public Class getDefaultType() {
return AbstractSourceCode.class;
}
/**
* Overrides getDynamicComponent
*
* @see org.openflexo.inspector.widget.DenaliWidget#getDynamicComponent()
*/
@Override
public JComponent getDynamicComponent() {
return panel;
}
/**
* Overrides updateModelFromWidget
*
* @see org.openflexo.inspector.widget.DenaliWidget#updateModelFromWidget()
*/
@Override
public void updateModelFromWidget() {
if (isUpdatingWidget) {
return;
}
isUpdatingModel = true;
try {
panel.updateModelFromWidget();
} finally {
isUpdatingModel = false;
}
super.updateModelFromWidget();
}
/**
* Overrides updateWidgetFromModel
*
* @see org.openflexo.inspector.widget.DenaliWidget#updateWidgetFromModel()
*/
@Override
public void updateWidgetFromModel() {
if (isUpdatingModel) {
return;
}
isUpdatingWidget = true;
try {
panel.updateWidgetFromModel();
} finally {
isUpdatingWidget = false;
}
}
@Override
public boolean defaultShouldExpandVertically() {
return true;
}
@Override
public void setModel(InspectableObject value) {
if (value != getModel()) {
panel.resetToDefault();
}
super.setModel(value);
}
}