/* * Copyright (c) 2014 tabletoptool.com team. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * rptools.com team - initial implementation * tabletoptool.com team - further development */ package com.t3.client.tool; import java.awt.Color; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; 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.event.MouseWheelEvent; import java.io.IOException; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JSlider; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.JTextComponent; import com.jeta.forms.components.colors.JETAColorWell; import com.jeta.forms.components.label.JETALabel; import com.jeta.forms.components.panel.FormPanel; import com.t3.client.AppState; import com.t3.client.ScreenPoint; import com.t3.client.TabletopTool; import com.t3.client.ui.zone.ZoneRenderer; import com.t3.image.ImageUtil; import com.t3.model.CellPoint; import com.t3.model.Zone; import com.t3.model.ZonePoint; import com.t3.model.grid.Grid; import com.t3.swing.SwingUtil; /** */ public class GridTool extends DefaultTool { private static final long serialVersionUID = 3760846783148208951L; private static final int zoomSliderStopCount = 100; private static enum Size { Increase, Decrease }; private final JSpinner gridSizeSpinner; private final JTextField gridOffsetXTextField; private final JTextField gridOffsetYTextField; private final JETAColorWell colorWell; private final JSlider zoomSlider; private final JTextField gridSecondDimension; private final JETALabel gridSecondDimensionLabel; private final FormPanel controlPanel; private int lastZoomIndex; private int dragOffsetX; private int dragOffsetY; private int mouseX; private int mouseY; private boolean oldShowGrid; public GridTool() { try { setIcon(new ImageIcon(ImageUtil.getImage("com/t3/client/image/grid.gif"))); } catch (IOException ioe) { ioe.printStackTrace(); } // Create the control panel controlPanel = new FormPanel("com/t3/client/ui/forms/adjustGridControlPanel.xml"); controlPanel.setBorder(BorderFactory.createLineBorder(Color.black)); gridSizeSpinner = controlPanel.getSpinner("gridSize"); gridSizeSpinner.setModel(new SpinnerNumberModel()); gridSizeSpinner.addChangeListener(new UpdateGridListener()); gridOffsetXTextField = controlPanel.getTextField("offsetX"); gridOffsetXTextField.addKeyListener(new UpdateGridListener()); gridOffsetYTextField = controlPanel.getTextField("offsetY"); gridOffsetYTextField.addKeyListener(new UpdateGridListener()); gridSecondDimensionLabel = (JETALabel) controlPanel.getLabel("gridSecondDimensionLabel"); gridSecondDimension = controlPanel.getTextField("gridSecondDimension"); gridSecondDimension.addFocusListener(new UpdateGridListener()); colorWell = (JETAColorWell) controlPanel.getComponentByName("colorWell"); colorWell.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { copyControlPanelToGrid(); } }); JButton closeButton = (JButton) controlPanel.getComponentByName("closeButton"); closeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { resetTool(); // Lee: just to make the light sources snap to their owners after the tool is closed Zone z = TabletopTool.getFrame().getCurrentZoneRenderer().getZone(); z.putTokens(z.getTokens()); } }); zoomSlider = (JSlider) controlPanel.getComponentByName("zoomSlider"); zoomSlider.setMinimum(0); zoomSlider.setMaximum(zoomSliderStopCount); ZoomChangeListener zoomListener = new ZoomChangeListener(); zoomSlider.addChangeListener(zoomListener); zoomSlider.addMouseListener(zoomListener); } @Override protected void installKeystrokes(Map<KeyStroke, Action> actionMap) { super.installKeystrokes(actionMap); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK), new GridSizeAction(Size.Decrease)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK), new GridSizeAction(Size.Decrease)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK), new GridSizeAction(Size.Increase)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK), new GridSizeAction(Size.Increase)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new GridOffsetAction(Direction.Up)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new GridOffsetAction(Direction.Left)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new GridOffsetAction(Direction.Down)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new GridOffsetAction(Direction.Right)); } private void copyGridToControlPanel() { Zone zone = renderer.getZone(); Grid grid = zone.getGrid(); updateSecondDimension(grid, true); gridSizeSpinner.setValue(grid.getSize()); gridOffsetXTextField.setText(Integer.toString(grid.getOffsetX())); gridOffsetYTextField.setText(Integer.toString(grid.getOffsetY())); colorWell.setColor(new Color(zone.getGridColor())); resetZoomSlider(); } /** * Updates the panel's text or the grids second settable dimension. * * @param toPanel * true = from grid to panel, false = from panel to grid * @param grid */ private void updateSecondDimension(Grid grid, boolean toPanel) { if (grid.getCapabilities().isSecondDimensionAdjustmentSupported()) { if (toPanel) { // truncate to 3 decimal places double secondDim = Math.round(grid.getSecondDimension() * 1000.0) / 1000.0; gridSecondDimension.setText(Double.toString(secondDim)); } else { // toGrid double newMajDiameter = getDouble(gridSecondDimension.getText(), 0); grid.setSecondDimension(newMajDiameter); } } } private void copyControlPanelToGrid() { Zone zone = renderer.getZone(); Grid grid = zone.getGrid(); updateSecondDimension(grid, false); grid.setSize(Math.max((Integer) gridSizeSpinner.getValue(), Grid.MIN_GRID_SIZE)); updateSecondDimension(grid, true); grid.setOffset(getInt(gridOffsetXTextField, 0), getInt(gridOffsetYTextField, 0)); zone.setGridColor(colorWell.getColor().getRGB()); renderer.repaint(); } @Override public String getTooltip() { return "tool.gridtool.tooltip"; } @Override public String getInstructions() { return "tool.gridtool.instructions"; } private int getInt(JTextComponent component, int defaultValue) { return getInt(component.getText(), defaultValue); } private int getInt(String value, int defaultValue) { try { return value.length() > 0 ? Integer.parseInt(value.trim()) : defaultValue; } catch (NumberFormatException e) { return 0; } } private double getDouble(String value, double defaultValue) { try { return value.length() > 0 ? Double.parseDouble(value.trim()) : defaultValue; } catch (NumberFormatException e) { return 0; } } /* * (non-Javadoc) * * @see tabletoptool.client.Tool#attachTo(tabletoptool.client.ZoneRenderer) */ @Override protected void attachTo(ZoneRenderer renderer) { oldShowGrid = AppState.isShowGrid(); AppState.setShowGrid(true); Grid grid = renderer.getZone().getGrid(); boolean showSecond = grid.getCapabilities().isSecondDimensionAdjustmentSupported() ? true : false; gridSecondDimension.setVisible(showSecond); gridSecondDimensionLabel.setVisible(showSecond); TabletopTool.getFrame().showControlPanel(controlPanel); renderer.repaint(); super.attachTo(renderer); copyGridToControlPanel(); } /* * (non-Javadoc) * * @see tabletoptool.client.Tool#detachFrom(tabletoptool.client.ZoneRenderer) */ @Override protected void detachFrom(ZoneRenderer renderer) { AppState.setShowGrid(oldShowGrid); TabletopTool.getFrame().hideControlPanel(); renderer.repaint(); // Commit the grid size change Zone zone = renderer.getZone(); TabletopTool.serverCommand().setZoneGridSize(zone.getId(), zone.getGrid().getOffsetX(), zone.getGrid().getOffsetY(), zone.getGrid().getSize(), zone.getGridColor()); super.detachFrom(renderer); } //// // MOUSE LISTENER @Override public void mousePressed(java.awt.event.MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { ZonePoint zp = new ScreenPoint(e.getX(), e.getY()).convertToZone(renderer); int x = zp.x - renderer.getZone().getGrid().getOffsetX(); int y = zp.y - renderer.getZone().getGrid().getOffsetY(); dragOffsetX = x % renderer.getZone().getGrid().getSize(); dragOffsetY = y % renderer.getZone().getGrid().getSize(); } else { super.mousePressed(e); } } //// // MOUSE MOTION LISTENER @Override public void mouseDragged(java.awt.event.MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { ZonePoint zp = new ScreenPoint(e.getX(), e.getY()).convertToZone(renderer); int x = zp.x - dragOffsetX; int y = zp.y - dragOffsetY; int gridSize = renderer.getZone().getGrid().getSize(); x %= gridSize; y %= gridSize; if (x > 0) x -= gridSize; if (y > 0) y -= gridSize; renderer.getZone().getGrid().setOffset(x, y); //renderer.repaint(); copyGridToControlPanel(); } else { super.mouseDragged(e); } } @Override public void mouseMoved(java.awt.event.MouseEvent e) { mouseX = e.getX(); mouseY = e.getY(); } //// // MOUSE WHEEL LISTENER /* * (non-Javadoc) * * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent) */ @Override public void mouseWheelMoved(MouseWheelEvent e) { ZoneRenderer renderer = (ZoneRenderer) e.getSource(); if (SwingUtil.isControlDown(e)) { if (e.getWheelRotation() > 0) { renderer.zoomOut(e.getX(), e.getY()); } else { renderer.zoomIn(e.getX(), e.getY()); } } else { if (e.getWheelRotation() > 0) { adjustGridSize(renderer, Size.Increase); } else { adjustGridSize(renderer, Size.Decrease); } } } private void resetZoomSlider() { EventQueue.invokeLater(new Runnable() { @Override public void run() { lastZoomIndex = zoomSliderStopCount / 2; zoomSlider.setValue(lastZoomIndex); } }); } private void adjustGridSize(ZoneRenderer renderer, Size direction) { CellPoint cell = renderer.getCellAt(new ScreenPoint(mouseX, mouseY)); if (cell == null) { return; } int oldGridSize = renderer.getZone().getGrid().getSize(); switch (direction) { case Increase: renderer.adjustGridSize(1); updateSecondDimension(renderer.getZone().getGrid(), true); if (renderer.getZone().getGrid().getSize() != oldGridSize) { renderer.moveGridBy(-cell.x, -cell.y); } break; case Decrease: renderer.adjustGridSize(-1); updateSecondDimension(renderer.getZone().getGrid(), true); if (renderer.getZone().getGrid().getSize() != oldGridSize) { renderer.moveGridBy(cell.x, cell.y); } break; } copyGridToControlPanel(); } private final class GridSizeAction extends AbstractAction { private static final long serialVersionUID = -3949586212099357034L; private final Size size; public GridSizeAction(Size size) { this.size = size; } @Override public void actionPerformed(ActionEvent e) { ZoneRenderer renderer = (ZoneRenderer) e.getSource(); adjustGridSize(renderer, size); copyGridToControlPanel(); } } private static enum Direction { Left, Right, Up, Down }; private class GridOffsetAction extends AbstractAction { private static final long serialVersionUID = 6664327737774374442L; private final Direction direction; public GridOffsetAction(Direction direction) { this.direction = direction; } @Override public void actionPerformed(ActionEvent e) { ZoneRenderer renderer = (ZoneRenderer) e.getSource(); switch (direction) { case Left: renderer.moveGridBy(-1, 0); break; case Right: renderer.moveGridBy(1, 0); break; case Up: renderer.moveGridBy(0, -1); break; case Down: renderer.moveGridBy(0, 1); break; } copyGridToControlPanel(); } } private class ZoomChangeListener extends MouseAdapter implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { int delta = zoomSlider.getValue() - lastZoomIndex; if (delta == 0) { return; } boolean direction = delta > 0; delta = Math.abs(delta); ZonePoint centerPoint = renderer.getCenterPoint(); for (int i = 0; i < delta; i++) { if (direction) { renderer.getZoneScale().zoomOut(centerPoint.x, centerPoint.y); } else { renderer.getZoneScale().zoomIn(centerPoint.x, centerPoint.y); } } lastZoomIndex = zoomSlider.getValue(); } @Override public void mouseReleased(MouseEvent e) { resetZoomSlider(); } } //// // ACTIONS private class UpdateGridListener implements KeyListener, ChangeListener, FocusListener { @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { copyControlPanelToGrid(); } @Override public void keyTyped(KeyEvent e) { } @Override public void stateChanged(ChangeEvent e) { copyControlPanelToGrid(); } @Override public void focusLost(FocusEvent e) { copyControlPanelToGrid(); } @Override public void focusGained(FocusEvent e) { } } }