package ca.sqlpower.swingui.querypen;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import org.apache.log4j.Logger;
import ca.sqlpower.object.SPVariableHelper;
import ca.sqlpower.query.Item;
import ca.sqlpower.swingui.querypen.MouseState.MouseStates;
import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolox.nodes.PStyledText;
import edu.umd.cs.piccolox.pswing.PSwing;
/**
* This PNode represents a SQL column on a table.
*/
public class UnmodifiableItemPNode extends PNode implements CleanupPNode {
private static final Logger logger = Logger.getLogger(UnmodifiableItemPNode.class);
/**
* The amount of space to place between the column name text and
* the where clause.
*/
private static final double WHERE_BUFFER = 5;
/**
* This text will go in the whereText field when there is
* no where clause on the current item.
*/
private static final String WHERE_START_TEXT = " ";
/**
* The item this node is displaying.
*/
private final Item item;
/**
* A text area to allow showing the name of the item and
* for editing its alias.
*/
private final EditablePStyledText columnText;
/**
* The text area storing the where clause for a given item.
*/
private final EditablePStyledTextWithOptionBox whereText;
/**
* Tracks if the item is in the select portion of a select
* statement.
*/
private final JCheckBox isInSelectCheckBox;
private boolean isJoined = false;
private boolean isInJoiningState = false;
private List<JoinLine> joinedLines;
/**
* These listeners will fire a change event when an element on this object
* is changed that affects the resulting generated query.
*/
private final Collection<PropertyChangeListener> queryChangeListeners;
/**
* A listener to properly display the alias and column name when the
* {@link EditablePStyledText} is switching from edit to non-edit mode and
* back. This listener for the nameEditor will show only the alias when the
* alias is being edited. When the alias is not being edited it will show
* the alias and column name, in brackets, if an alias is specified.
* Otherwise only the column name will be displayed.
*/
private EditStyledTextListener editingTextListener = new EditStyledTextListener() {
/**
* Tracks if we are in an editing state or not. Used to keep the
* editingStopped method from running only once per stop edit (some
* cases the editingStopped can be called from multiple places on the
* same stopEditing).
*/
private boolean editing = false;
public void editingStopping() {
String alias = item.getAlias();
if (editing) {
JEditorPane nameEditor = columnText.getEditorPane();
alias = nameEditor.getText();
if (!(nameEditor.getText() != null && nameEditor.getText().length() > 0 && !nameEditor.getText().equals(item.getName()))) {
alias = "";
}
logger.debug("editor has text " + nameEditor.getText() + " alias is " + alias);
}
if(isJoined) {
highLightText();
}
item.setAlias(alias);
if (editing) {
setVisibleAliasText();
}
editing = false;
}
public void editingStarting() {
editing = true;
if (item.getAlias() != null && item.getAlias().length() > 0) {
columnText.getEditorPane().setText(item.getAlias());
logger.debug("Setting editor text to " + item.getAlias());
}
}
};
private EditStyledTextListener whereTextListener = new EditStyledTextListener() {
public void editingStopping() {
item.setWhere(getWhereText());
}
public void editingStarting() {
//do nothing
}
};
/**
* This listener will update this view component with changes to its model.
* Events will also be echoed which may not be used in the future.
*/
private PropertyChangeListener itemChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("selected")) {
isInSelectCheckBox.setSelected(item.isSelected());
} else if (evt.getPropertyName().equals("alias")) {
setVisibleAliasText();
} else if (evt.getPropertyName().equals("where")) {
if (!item.getWhere().equals(whereText.getEditorPane().getText().trim())) {
whereText.getEditorPane().setText(item.getWhere());
fillWithSpaces();
whereText.syncWithDocument();
whereText.repaint();
}
}
for (PropertyChangeListener l : queryChangeListeners) {
l.propertyChange(evt);
}
}
};
private void fillWithSpaces() {
if (whereText.getEditorPane().getText() == null || whereText.getEditorPane().getText().trim().equals("") ) {
whereText.getEditorPane().setText(WHERE_START_TEXT);
} else if ( whereText.getEditorPane().getText().length() < WHERE_START_TEXT.length()) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < WHERE_START_TEXT.length() - whereText.getEditorPane().getText().length(); i++) {
sb.append(" ");
}
whereText.getEditorPane().setText(whereText.getEditorPane().getText() + sb.toString());
}
}
/**
* HighLights the columnText. This will be called when the item is joined or Deleted.
*/
public void highLightText() {
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
if(isJoined) {
StyleConstants.setForeground(attributeSet, Color.blue);
} else {
StyleConstants.setForeground(attributeSet, Color.black);
}
DefaultStyledDocument doc = (DefaultStyledDocument)columnText.getDocument();
doc.setCharacterAttributes(0, doc.getLength(), attributeSet, false);
columnText.repaint();
columnText.syncWithDocument();
}
public void JoinTo(JoinLine line) {
joinedLines.add(line);
setIsJoined(true);
isInJoiningState = false;
}
public void setJoiningState(boolean state){
if(!state) {
setPaint(new Color(0x00ffffff, true));
repaint();
}
isInJoiningState = state;
}
public List<JoinLine> getJoinedLines() {
return joinedLines;
}
public void removeJoinedLine(JoinLine line){
joinedLines.remove(line);
if(joinedLines.isEmpty()) {
setIsJoined(false);
}
}
/**
* The check box for selection wrapped as a PSwing
* object.
*/
private PSwing swingCheckBox;
private final QueryPen queryPen;
private final SPVariableHelper variablesHelper;
public UnmodifiableItemPNode(QueryPen mouseStates, PCanvas canvas, Item i) {
this(mouseStates, canvas, i, null);
}
public UnmodifiableItemPNode(QueryPen mouseStates, PCanvas canvas, Item i, SPVariableHelper newVariablesHelper) {
this.item = i;
this.variablesHelper = newVariablesHelper;
item.addPropertyChangeListener(itemChangeListener);
queryPen = mouseStates;
queryChangeListeners = new ArrayList<PropertyChangeListener>();
joinedLines = new ArrayList<JoinLine>();
isInSelectCheckBox = new JCheckBox();
isInSelectCheckBox.setOpaque(false);
swingCheckBox = new PSwing(isInSelectCheckBox);
addChild(swingCheckBox);
isInSelectCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (isInSelectCheckBox.isSelected()) {
queryPen.getModel().selectItem(item);
} else {
queryPen.getModel().unselectItem(item);
}
}
});
columnText = new EditablePStyledText(item.getName(), queryPen, canvas);
columnText.addEditStyledTextListener(editingTextListener);
double textYTranslation = (swingCheckBox.getFullBounds().height - columnText.getFullBounds().height)/2;
columnText.translate(swingCheckBox.getFullBounds().width, textYTranslation);
addInputEventListener(new PBasicInputEventHandler() {
@Override
public void mouseEntered(PInputEvent event) {
if(queryPen.getMouseState().equals(MouseStates.CREATE_JOIN)) {
setPaint(Color.GRAY);
repaint();
}
}
@Override
public void mouseExited(PInputEvent event) {
if(!isInJoiningState) {
setPaint(new Color(0x00ffffff, true));
repaint();
}
}
});
addChild(columnText);
whereText = new EditablePStyledTextWithOptionBox(WHERE_START_TEXT, queryPen, canvas, WHERE_START_TEXT.length(), variablesHelper);
whereText.addEditStyledTextListener(whereTextListener);
whereText.translate(0, textYTranslation);
addChild(whereText);
logger.debug("Pnode " + item.getName() + " created.");
setWidth(getFullBounds().getWidth());
setHeight(getFullBounds().getHeight());
isInSelectCheckBox.setSelected(item.isSelected());
if (item.getWhere().trim().length() > 0) {
whereText.getEditorPane().setText(item.getWhere());
whereText.syncWithDocument();
}
setVisibleAliasText();
}
/**
* Sets the visible column name text to have an alias if it exists in the model.
*/
private void setVisibleAliasText() {
JEditorPane nameEditor = columnText.getEditorPane();
if (item.getAlias() == null || item.getAlias().trim().length() <= 0) {
logger.debug("item name is " + item.getName());
nameEditor.setText(item.getName());
} else {
nameEditor.setText(item.getAlias() + " (" + item.getName() + ")");
}
columnText.syncWithDocument();
}
public void setIsJoined(boolean joined) {
isJoined = joined;
setPaint(new Color(0x00ffffff, true));
repaint();
highLightText();
}
public Item getItem() {
return item;
}
public PStyledText getItemText() {
return columnText;
}
public PStyledText getWherePStyledText() {
return whereText;
}
public String getWhereText() {
String text = whereText.getEditorPane().getText();
if (text.equals(WHERE_START_TEXT)) {
return "";
} else {
return text;
}
}
public void setInSelected(boolean selected) {
isInSelectCheckBox.setSelected(selected);
if (selected) {
queryPen.getModel().selectItem(item);
} else {
queryPen.getModel().unselectItem(item);
}
}
public boolean isInSelect() {
return isInSelectCheckBox.isSelected();
}
public void addQueryChangeListener(PropertyChangeListener l) {
queryChangeListeners.add(l);
}
public void removeQueryChangeListener(PropertyChangeListener l) {
queryChangeListeners.remove(l);
}
public double getDistanceForWhere() {
return swingCheckBox.getFullBounds().width + columnText.getWidth() + WHERE_BUFFER;
}
public void positionWhere(double xpos) {
logger.debug("Moving where text: xpos = " + xpos + ", text x position = " + whereText.getFullBounds().getX() + " x offset " + whereText.getXOffset());
whereText.translate(xpos - whereText.getXOffset(), 0);
setWidth(getFullBounds().getWidth());
setHeight(getFullBounds().getHeight());
}
public void cleanup() {
item.removePropertyChangeListener(itemChangeListener);
}
public Item getModel() {
return item;
}
}