/*
* Copyright (c) 2008, SQL Power Group Inc.
*
* This file is part of Wabit.
*
* Wabit 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.
*
* Wabit 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.sqlpower.swingui.querypen;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.log4j.Logger;
import ca.sqlpower.object.SPObject;
import ca.sqlpower.object.SPVariableHelper;
import ca.sqlpower.query.Container;
import ca.sqlpower.query.Item;
import ca.sqlpower.query.Query;
import ca.sqlpower.query.QueryChangeEvent;
import ca.sqlpower.query.QueryChangeListener;
import ca.sqlpower.query.SQLJoin;
import ca.sqlpower.query.TableContainer;
import ca.sqlpower.sql.jdbcwrapper.DatabaseMetaDataDecorator;
import ca.sqlpower.sql.jdbcwrapper.DatabaseMetaDataDecorator.CacheType;
import ca.sqlpower.sqlobject.SQLDatabase;
import ca.sqlpower.sqlobject.SQLObject;
import ca.sqlpower.sqlobject.SQLObjectException;
import ca.sqlpower.sqlobject.SQLRelationship;
import ca.sqlpower.sqlobject.SQLRelationship.ColumnMapping;
import ca.sqlpower.sqlobject.SQLRelationship.SQLImportedKey;
import ca.sqlpower.sqlobject.SQLTable;
import ca.sqlpower.swingui.CursorManager;
import ca.sqlpower.swingui.dbtree.SQLObjectSelection;
import ca.sqlpower.swingui.querypen.event.CreateJoinEventHandler;
import ca.sqlpower.swingui.querypen.event.QueryPenSelectionEventHandler;
import ca.sqlpower.util.TransactionEvent;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.util.PPaintContext;
import edu.umd.cs.piccolox.pswing.PSwingCanvas;
import edu.umd.cs.piccolox.swing.PScrollPane;
/**
* The pen where users can graphically create sql queries.
*/
public class QueryPen implements MouseState {
private static Logger logger = Logger.getLogger(QueryPen.class);
private static final Color SELECTION_COLOUR = new Color(0xcc333333);
private static final String DELETE_ACTION = "Delete";
private static final String BACKSPACE_ACTION = "Backspace";
private static final String ZOOM_IN_ACTION = "Zoom In";
private static final String ZOOM_OUT_ACTION = "Zoom Out";
private static final String JOIN_ACTION = "Create Join";
public static final Color SELECTED_CONTAINER_COLOUR = new Color(0xff9900);
public static final Color SELECTED_CONTAINER_GRADIENT_COLOUR = new Color(0xffcc66);
public static final Color UNSELECTED_CONTAINER_COLOUR = new Color(0x999999);
public static final Color UNSELECTED_CONTAINER_GRADIENT_COLOUR = new Color(0xcccccc);
public static final float CONTAINER_ROUND_CORNER_RADIUS = 8f;
public static final Color WHERE_BACKGROUND_COLOUR = new Color(0xeeeeee);
private static final String QUERY_EXECUTE = "Execute";
private JPanel panel;
private AbstractAction zoomInAction;
private AbstractAction zoomOutAction;
private final class QueryPenDropTargetListener implements
DropTargetListener {
public void dropActionChanged(DropTargetDragEvent dtde) {
//no-op
}
public void drop(DropTargetDropEvent dtde) {
if (!dtde.isLocalTransfer()) {
logger.debug("Rejecting non-local transfer");
dtde.rejectDrop();
return;
}
if (!dtde.isDataFlavorSupported(SQLObjectSelection.LOCAL_SQLOBJECT_ARRAY_FLAVOUR)) {
logger.debug("Rejecting transfer of unknown flavour");
dtde.rejectDrop();
return;
}
SQLObject[] draggedObjects;
try {
draggedObjects = (SQLObject[]) dtde.getTransferable().getTransferData(SQLObjectSelection.LOCAL_SQLOBJECT_ARRAY_FLAVOUR);
} catch (UnsupportedFlavorException e) {
dtde.dropComplete(false);
throw new RuntimeException(e);
} catch (IOException e) {
dtde.dropComplete(false);
throw new RuntimeException(e);
}
SQLDatabase tableDatabase = null;
int response = 0;
for (Object draggedSQLObject : draggedObjects) {
if (draggedSQLObject instanceof SQLTable) {
SQLTable table = (SQLTable) draggedSQLObject;
if(tableDatabase != null) {
if(!tableDatabase.equals(table.getParentDatabase())) {
JOptionPane.showMessageDialog(null, "The tables your are adding are from different database connections. " +
"This will cause errors in your query.", "Error", JOptionPane.ERROR_MESSAGE);
response = 1;
}
}
tableDatabase = table.getParentDatabase();
} else {
logger.debug("Rejecting drop of non-SQLTable SQLObject: " + draggedSQLObject);
dtde.rejectDrop();
return;
}
}
//Check to see if the table being dragged in the selected database or not
if(!getModel().getDatabase().equals(tableDatabase) && response == 0) {
String message;
if(draggedObjects.length == 1) {
message = "The table being added is not from \"" + getModel().getDatabase() + "\".";
} else {
message = "The tables being added are not from \"" + getModel().getDatabase() + "\".";
}
message += "\nDo you want to change your connection to \"" + tableDatabase + "\"?\n" +
"If so, your current query will be cleared and the new tables added.";
String options[] = {"YES","NO"};
response = JOptionPane.showOptionDialog(null, message, "Warning",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, options, options[1]);
if(response == 0) {
model.setDataSource(tableDatabase.getDataSource());
model.reset();
}
}
if(response == 0) {
for (Object draggedSQLObject : draggedObjects) {
try {
cursorManager.startWaitMode();
SQLTable table = (SQLTable) draggedSQLObject;
model.startCompoundEdit("Table " + table.getName() + " was dropped " +
"on the query pen, adding it to the mode.");
DatabaseMetaDataDecorator.putHint(DatabaseMetaDataDecorator.CACHE_TYPE, CacheType.EAGER_CACHE);
TableContainer tableModel = new TableContainer(QueryPen.this.model.getDatabase(), table);
Point location = dtde.getLocation();
Point2D movedLoc = canvas.getCamera().localToView(location);
tableModel.setPosition(movedLoc);
int aliasCounter = 0;
ArrayList<String> aliasNames = new ArrayList<String>();
// This basically check if there exist a table with the same name as the one being dropped
// compare all the alias names to see which number it needs to not create a duplicate table name.
for (Container existingTable: model.getFromTableList()) {
if (tableModel.getName().equals(existingTable.getName())){
logger.debug("Found same tableName, going to alias");
aliasNames.add(existingTable.getAlias());
}
}
Collections.sort(aliasNames);
for(String alias : aliasNames) {
if(alias.equals(tableModel.getName()+ "_"+ aliasCounter)
|| alias.equals("")) {
aliasCounter++;
}
}
if(aliasCounter != 0) {
tableModel.setAlias(tableModel.getName()+ "_"+ aliasCounter);
}
QueryPen.this.model.addTable(tableModel);
try {
for (SQLRelationship relation : table.getExportedKeys()) {
List<Container> fkContainers = getContainerPane(relation.getFkTable());
for (Container fkContainer : fkContainers) {
for (ColumnMapping mapping : relation.getChildren(ColumnMapping.class)) {
logger.debug("PK container has model name " + tableModel.getName() +
" looking for col named " + mapping.getPkColumn().getName());
Item pkItemNode = tableModel.getItem(mapping.getPkColumn());
Item fkItemNode = fkContainer.getItem(mapping.getFkColumn());
logger.debug("FK item node is " + fkItemNode);
if (pkItemNode != null && fkItemNode != null) {
if (pkItemNode.getParent() != fkItemNode.getParent()) {
SQLJoin join = new SQLJoin(pkItemNode, fkItemNode);
join.addJoinChangeListener(queryChangeListener);
QueryPen.this.model.addJoin(join);
} else {
logger.debug("we don't allow items joining on the same table");
}
} else {
throw new IllegalStateException("Trying to join two columns, one of which does not exist");
}
}
}
}
for (SQLImportedKey key : table.getImportedKeys()) {
SQLRelationship relation = key.getRelationship();
List<Container> pkContainers = getContainerPane(relation.getParent());
for (Container pkContainer : pkContainers) {
for (ColumnMapping mapping : relation.getChildren(ColumnMapping.class)) {
Item fkItemNode = pkContainer.getItem(mapping.getPkColumn());
Item pkItemNode = tableModel.getItem(mapping.getFkColumn());
if (pkItemNode != null && fkItemNode != null) {
if (pkItemNode.getParent() != fkItemNode.getParent()) {
SQLJoin join = new SQLJoin(fkItemNode, pkItemNode);
join.addJoinChangeListener(queryChangeListener);
QueryPen.this.model.addJoin(join);
} else {
logger.debug("we don't allow items joining on the same table");
}
} else {
throw new IllegalStateException("Trying to join two columns, one of which does not exist");
}
}
}
}
} catch (SQLObjectException e) {
throw new RuntimeException(e);
}
for (Item itemNode : tableModel.getItems()) {
model.selectItem(itemNode);
}
dtde.acceptDrop(dtde.getDropAction());
dtde.dropComplete(true);
} finally {
cursorManager.finishWaitMode();
DatabaseMetaDataDecorator.putHint(DatabaseMetaDataDecorator.CACHE_TYPE, CacheType.NO_CACHE);
model.endCompoundEdit();
}
}
}
}
public void dragOver(DropTargetDragEvent dtde) {
//no-op
}
public void dragExit(DropTargetEvent dte) {
//no-op
}
public void dragEnter(DropTargetDragEvent dtde) {
//no-op
}
}
private static final float SELECTION_TRANSPARENCY = 0.33f;
/**
* The scroll pane that contains the visual query a user is building.
*/
private final JScrollPane scrollPane;
/**
* The Piccolo canvas that allows zooming and the JComponents are placed in.
*/
private final PSwingCanvas canvas;
/**
* The layer that contains all of the join lines. This will be behind the top layer.
*/
private final PLayer joinLayer;
/**
* The top layer that has the tables and columns added to it. This should be used
* instead of getting the first layer from the canvas.
*/
private final PLayer topLayer;
private final JButton createJoinButton;
/**
* This text area is for any part of the WHERE clause
* that a user would want to add in that is not specific
* to a column in a table.
*/
private final JTextField globalWhereText;
private final String acceleratorKeyString;
/**
* This is the queryPen's model
*/
private final Query model;
/**
* The mouse state in this query pen.
*/
private MouseStates mouseState = MouseStates.READY;
/**
* A SelectionEventHandler that supports multiple select on Tables for deletion and dragging.
*/
private QueryPenSelectionEventHandler selectionEventHandler;
/**
* The cursor manager for this Query pen.
*/
private final CursorManager cursorManager;
/**
* This is the container pane that will hold constants to allow users to join on special
* things or add unusual values to a select statement.
*/
private ConstantsPane constantsContainer;
/**
* This slider will zoom the canvas in and out.
*/
private JSlider zoomSlider;
/**
* This panel contains the zoom slider and its associated images.
*/
private JPanel zoomSliderContainer;
/**
* This action will be called when the query defined in this query pen is
* to be executed.
*/
private final Action executeQueryAction;
/**
* Deletes the selected item from the QueryPen.
*/
private final Action deleteAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
try {
model.startCompoundEdit("Deleting objects from the query pen.");
Iterator<?> selectedIter = selectionEventHandler.getSelection().iterator();
while(selectedIter.hasNext()) {
PNode pickedNode = (PNode) selectedIter.next();
if (pickedNode.getParent() == topLayer) {
if (pickedNode == constantsContainer) {
return;
}
if (pickedNode instanceof ContainerPane) {
ContainerPane pane = ((ContainerPane)pickedNode);
deleteContainer(pane);
}
}
if (pickedNode.getParent() == joinLayer) {
deleteJoinLine((JoinLine)pickedNode);
}
}
} finally {
model.endCompoundEdit();
}
}
};
/**
* This will delete the given container from the model.
*/
public void deleteContainer(ContainerPane pickedNode) {
model.removeTable(pickedNode.getModel());
}
/**
* This method will remove the Joined line from its left and right Nodes and
* remove it from the joinLayer.
*/
public void deleteJoinLine(JoinLine pickedNode) {
model.removeJoin(pickedNode.getModel());
}
/**
* Listeners that will be notified when the query string has been modified.
*/
private List<PropertyChangeListener> queryListeners = new ArrayList<PropertyChangeListener>();
/**
* This change listener will be invoked whenever a change is made to the query pen
* that will result in a change to the SQL script.
*
* XXX Does anything use this?
*/
private PropertyChangeListener queryChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
logger.debug("Query pen received state change.");
for (PropertyChangeListener l : queryListeners) {
l.propertyChange(evt);
}
}
};
private CreateJoinEventHandler joinCreationListener;
/**
* This button will execute the query defined in the query pen.
*/
private final JButton playPenExecuteButton;
/**
* This button deletes the selected item in the query pen.
*/
private final JButton deleteButton;
/**
* This button resets the query editor back to how it was originally
* setup.
*/
private final JButton resetButton;
/**
* This action will reset the query.
*/
private final Action resetAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
model.reset();
}
};
/**
* This toolbar will be placed on top of the query pen.
*/
private JToolBar queryPenToolBar;
/**
* This listener will be attached to the query that is the model of this
* component and handle all query changes.
*/
private final QueryChangeListener queryListener = new QueryChangeListener() {
public void propertyChangeEvent(PropertyChangeEvent evt) {
//do nothing
}
public void joinRemoved(QueryChangeEvent evt) {
SQLJoin removedJoin = evt.getJoinChanged();
for (Object node : joinLayer.getAllNodes()) {
if (node instanceof JoinLine && ((JoinLine) node).getModel() == removedJoin) {
JoinLine pickedNode = (JoinLine) node;
pickedNode.disconnectJoin();
joinLayer.removeChild(pickedNode);
break;
}
}
}
public void joinPropertyChangeEvent(PropertyChangeEvent evt) {
//do nothing
}
public void joinAdded(QueryChangeEvent evt) {
SQLJoin sqlJoin = evt.getJoinChanged();
JoinLine join = new JoinLine(QueryPen.this, canvas, sqlJoin);
joinLayer.addChild(join);
}
public void itemRemoved(QueryChangeEvent evt) {
//do nothing
}
public void itemPropertyChangeEvent(PropertyChangeEvent evt) {
//do nothing
}
public void itemAdded(QueryChangeEvent evt) {
//do nothing
}
public void containerRemoved(QueryChangeEvent evt) {
Container removedContainer = evt.getContainerChanged();
ContainerPane removedPane = null;
for (int i = 0; i < topLayer.getChildrenCount(); i++) {
final PNode child = topLayer.getChild(i);
if (child instanceof ContainerPane &&
((ContainerPane) child).getModel().equals(removedContainer)) {
removedPane = ((ContainerPane) child);
break;
}
}
if (removedPane == null) {
JOptionPane.showMessageDialog(getCanvas(), "Cannot find the table "
+ removedContainer.getName() + " to remove from the query pen.",
"Cannot find table.", JOptionPane.ERROR_MESSAGE);
return;
}
topLayer.removeChild(removedPane);
removedPane.removeQueryChangeListener(queryChangeListener);
}
public void containerAdded(QueryChangeEvent evt) {
Container containerChanged = evt.getContainerChanged();
ContainerPane pane = new ContainerPane(QueryPen.this, canvas, containerChanged, variablesHelper);
pane.addQueryChangeListener(queryChangeListener);
topLayer.addChild(pane);
canvas.repaint();
}
public void compoundEditEnded(TransactionEvent evt) {
//do nothing
}
public void compoundEditStarted(TransactionEvent evt) {
//do nothing
}
};
/**
* This action will allow users to create a join between two columns in
* the query.
*/
private final Action joinAction;
private SPVariableHelper variablesHelper;
public JPanel createQueryPen() {
panel.setLayout(new BorderLayout());
panel.add(getScrollPane(), BorderLayout.CENTER);
getPlayPenExecuteButton().setToolTipText(QUERY_EXECUTE + "(Shortcut "+ getAcceleratorKeyString()+ " R)");
canvas.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
, QUERY_EXECUTE);
canvas.getActionMap().put(QUERY_EXECUTE, executeQueryAction);
panel.setBackground(Color.WHITE);
return panel;
}
private JToolBar createToolBar() {
JToolBar queryPenBarChild = new JToolBar(JToolBar.HORIZONTAL);
queryPenBarChild.setFloatable(false);
queryPenBarChild.add(getPlayPenExecuteButton());
queryPenBarChild.addSeparator();
queryPenBarChild.add(getResetButton());
queryPenBarChild.add(getDeleteButton());
queryPenBarChild.add(getCreateJoinButton());
queryPenBarChild.addSeparator();
queryPenBarChild.add(getZoomSliderContainer());
queryPenBarChild.addSeparator();
return queryPenBarChild;
}
/**
* Creates a query pen set up with the given model.
*
* @param executeQueryAction
* This action will be used to execute the query built in the
* query pen.
* @param model
* This model will be used to set up the query pen initially and
* save changes to when the query pen changes.
* @param toolBar
* This tool bar will be the tool bar defined to be at the top of
* the query pen. If the tool bar given is null a default tool
* bar will be created.
*/
public QueryPen(Action executeQueryAction, Query model) {
this(executeQueryAction, model, true);
}
/**
* Creates a query pen set up with the given model.
*
* @param executeQueryAction
* This action will be used to execute the query built in the
* query pen.
* @param model
* This model will be used to set up the query pen initially and
* save changes to when the query pen changes.
* @param toolBar
* This tool bar will be the tool bar defined to be at the top of
* the query pen. If the tool bar given is null a default tool
* bar will be created.
* @param showConstantContainer
* This value should be set to true if the constants container is
* to be shown in the query pen. In some cases we don't want to
* show the constants pane as we are only interested in using
* actual tables. The container still exists and gets hooked up
* but it is just not displayed.
*/
public QueryPen(Action executeQueryAction, Query model, boolean showConstantContainer) {
playPenExecuteButton = new JButton(executeQueryAction);
deleteButton = new JButton(getDeleteAction());
resetButton = new JButton(getResetAction());
final ImageIcon deleteIcon = new ImageIcon(QueryPen.class.getClassLoader().getResource("ca/sqlpower/swingui/querypen/delete.png"));
getDeleteButton().setToolTipText(DELETE_ACTION+ " (Shortcut Delete)");
getDeleteButton().setIcon(deleteIcon);
this.executeQueryAction = executeQueryAction;
this.model = model;
model.addQueryChangeListener(queryListener);
panel = new JPanel();
cursorManager = new CursorManager(panel);
if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) {
acceleratorKeyString = "Cmd";
} else {
acceleratorKeyString = "Ctrl";
}
if (model instanceof SPObject) {
this.variablesHelper = new SPVariableHelper((SPObject)model);
} else {
this.variablesHelper = null;
}
canvas = new PSwingCanvas();
canvas.setBackground(Color.black);
canvas.setAnimatingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
canvas.setInteractingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
scrollPane = new PScrollPane(canvas);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.getHorizontalScrollBar().setUnitIncrement(10);
canvas.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), DELETE_ACTION);
canvas.getActionMap().put(DELETE_ACTION, getDeleteAction());
if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) {
canvas.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), BACKSPACE_ACTION);
canvas.getActionMap().put(BACKSPACE_ACTION, getDeleteAction());
}
canvas.setPanEventHandler( null );
topLayer = canvas.getLayer();
joinLayer = new PLayer();
canvas.getRoot().addChild(joinLayer);
canvas.getCamera().addLayer(0, joinLayer);
final int defaultSliderValue = 500;
zoomSlider = new JSlider(JSlider.HORIZONTAL, 1, 1000, defaultSliderValue);
zoomSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
final double newScale = (double)zoomSlider.getValue()/defaultSliderValue;
final PCamera camera = canvas.getCamera();
double oldScale = camera.getViewScale();
camera.scaleViewAboutPoint(newScale/oldScale, camera.getViewBounds().getCenterX(), camera.getViewBounds().getCenterY());
logger.debug("Camera scaled by " + newScale/oldScale + " and is now at " + camera.getViewScale());
}
});
zoomSlider.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if ((e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) > 0) {
zoomSlider.setValue(defaultSliderValue);
}
}
});
zoomInAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
zoomSlider.setValue(zoomSlider.getValue() + 50);
}
};
zoomSliderContainer = new JPanel(new BorderLayout());
zoomSliderContainer.setMaximumSize(new Dimension((int)zoomSlider.getPreferredSize().getWidth(), 200));
zoomSliderContainer.add(zoomSlider, BorderLayout.CENTER);
zoomSliderContainer.add(new JLabel(new ImageIcon(QueryPen.class.getClassLoader().getResource("ca/sqlpower/swingui/querypen/zoom_in16.png"))), BorderLayout.EAST);
zoomSliderContainer.add(new JLabel(new ImageIcon(QueryPen.class.getClassLoader().getResource("ca/sqlpower/swingui/querypen/zoom_out16.png"))), BorderLayout.WEST);
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | KeyEvent.SHIFT_MASK)
, ZOOM_IN_ACTION);
panel.getActionMap().put(ZOOM_IN_ACTION, zoomInAction);
zoomOutAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
zoomSlider.setValue(zoomSlider.getValue() - 50);
}
};
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | KeyEvent.SHIFT_MASK)
, ZOOM_OUT_ACTION);
panel.getActionMap().put(ZOOM_OUT_ACTION, zoomOutAction);
ImageIcon joinIcon = new ImageIcon(QueryPen.class.getClassLoader().getResource("ca/sqlpower/swingui/querypen/j.png"));
joinAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
setMouseState(MouseStates.CREATE_JOIN);
cursorManager.placeModeStarted();
}
};
createJoinButton = new JButton(getJoinAction());
createJoinButton.setToolTipText(JOIN_ACTION + " (Shortcut "+ acceleratorKeyString+ " J)");
createJoinButton.setIcon(joinIcon);
canvas.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_J, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
, JOIN_ACTION);
canvas.getActionMap().put(JOIN_ACTION, getJoinAction());
joinCreationListener = new CreateJoinEventHandler(this, canvas, cursorManager);
canvas.addInputEventListener(joinCreationListener);
joinCreationListener.addCreateJoinListener(queryChangeListener);
new DropTarget(canvas, new QueryPenDropTargetListener());
List<PLayer> layerList = new ArrayList<PLayer>();
layerList.add(topLayer);
layerList.add(joinLayer);
selectionEventHandler = new QueryPenSelectionEventHandler(topLayer, layerList);
selectionEventHandler.setMarqueePaint(SELECTION_COLOUR);
selectionEventHandler.setMarqueePaintTransparency(SELECTION_TRANSPARENCY);
canvas.addInputEventListener(selectionEventHandler);
globalWhereText = new JTextField();
globalWhereText.addFocusListener(new FocusListener() {
public void focusLost(FocusEvent e) {
queryChangeListener.propertyChange(new PropertyChangeEvent(globalWhereText, Container.PROPERTY_WHERE_MODIFIED, globalWhereText.getText(), globalWhereText.getText()));
}
public void focusGained(FocusEvent e) {
//do nothing
}
});
globalWhereText.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
//Do Nothing
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
queryChangeListener.propertyChange(new PropertyChangeEvent(globalWhereText, Container.PROPERTY_WHERE_MODIFIED, globalWhereText.getText(), globalWhereText.getText()));
}
}
public void keyPressed(KeyEvent e) {
//Do nothing
}
});
setQueryPenToolBar(createToolBar());
loadQueryCache(showConstantContainer);
}
/**
* This will load the related tables and their properties into the QueryPen.
*/
private void loadQueryCache(boolean showConstantContainer) {
constantsContainer = new ConstantsPane(this, canvas, model.getConstantsContainer(), this.variablesHelper);
constantsContainer.addChangeListener(queryChangeListener);
if (showConstantContainer) {
topLayer.addChild(constantsContainer);
}
Map<Item, UnmodifiableItemPNode> loadedItemPNodes = new HashMap<Item, UnmodifiableItemPNode>();
for (Container c : model.getFromTableList()) {
ContainerPane container = new ContainerPane(this, canvas, c, this.variablesHelper);
topLayer.addChild(container);
for (UnmodifiableItemPNode node : container.getContainedItems()) {
loadedItemPNodes.put(node.getItem(), node);
}
}
for (SQLJoin join : model.getJoins()) {
joinLayer.addChild(new JoinLine(QueryPen.this, canvas, join));
}
}
public QueryPenSelectionEventHandler getMultipleSelectEventHandler(){
return selectionEventHandler;
}
public JScrollPane getScrollPane() {
return scrollPane;
}
public JSlider getZoomSlider() {
return zoomSlider;
}
public JPanel getZoomSliderContainer() {
return zoomSliderContainer;
}
public JButton getCreateJoinButton() {
return createJoinButton;
}
public PSwingCanvas getCanvas() {
return canvas;
}
public MouseStates getMouseState() {
return mouseState;
}
public PLayer getJoinLayer() {
return joinLayer;
}
public PLayer getTopLayer() {
return topLayer;
}
public synchronized void setMouseState(MouseStates mouseState) {
this.mouseState = mouseState;
}
public Action getDeleteAction() {
return deleteAction;
}
public Action getResetAction() {
return resetAction;
}
public JTextField getGlobalWhereText() {
return globalWhereText;
}
public CursorManager getCursorManager() {
return cursorManager;
}
/**
* Returns a list of containers, where each one wraps the same
* SQLTable, in the Query model. If no containers wraps the SQLTable in
* the Query model then this will return an empty list.
*/
private List<Container> getContainerPane(SQLTable table) {
List<Container> containerList = new ArrayList<Container>();
for (Container node : model.getFromTableList()) {
if (node.getContainedObject() == table) {
containerList.add(node);
}
}
return containerList;
}
public void addQueryListener(PropertyChangeListener l) {
queryListeners.add(l);
}
public void removeQueryListener(PropertyChangeListener l) {
queryListeners.remove(l);
}
public Query getModel() {
return model;
}
public String getAcceleratorKeyString () {
return acceleratorKeyString;
}
public PSwingCanvas getQueryPenCanvas () {
return canvas;
}
public void cleanup() {
model.removeQueryChangeListener(queryListener);
queryListeners.clear();
for (Object o : topLayer.getAllNodes()) {
if (o instanceof CleanupPNode) {
((CleanupPNode)o).cleanup();
}
}
for (Object o : joinLayer.getAllNodes()) {
if (o instanceof CleanupPNode) {
((CleanupPNode)o).cleanup();
}
}
}
public void setZoom(int zoomLevel) {
if (zoomLevel > zoomSlider.getMinimum() && zoomLevel < zoomSlider.getMaximum()) {
zoomSlider.setValue( zoomLevel);
}
}
public JButton getPlayPenExecuteButton() {
return playPenExecuteButton;
}
public JButton getDeleteButton() {
return deleteButton;
}
public void setQueryPenToolBar(JToolBar queryPenToolBar) {
this.queryPenToolBar = queryPenToolBar;
}
public JToolBar getQueryPenToolBar() {
return queryPenToolBar;
}
public void setExecuteIcon(ImageIcon icon) {
getPlayPenExecuteButton().setIcon(icon);
}
public JButton getResetButton() {
return resetButton;
}
public Action getExecuteQueryAction() {
return executeQueryAction;
}
public Action getJoinAction() {
return joinAction;
}
}