/* * 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.ui; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Area; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JColorChooser; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.KeyStroke; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.JTextComponent; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.t3.client.AppUtil; import com.t3.client.T3Util; import com.t3.client.TabletopTool; import com.t3.client.ui.token.BarTokenOverlay; import com.t3.client.ui.token.BooleanTokenOverlay; import com.t3.client.ui.zone.FogUtil; import com.t3.client.ui.zone.ZoneRenderer; import com.t3.guid.GUID; import com.t3.language.I18N; import com.t3.model.CellPoint; import com.t3.model.ExposedAreaMetaData; import com.t3.model.MacroButtonProperties; import com.t3.model.Path; import com.t3.model.Player; import com.t3.model.Player.Role; import com.t3.model.initiative.InitiativeList; import com.t3.model.initiative.InitiativeValue; import com.t3.model.initiative.InitiativeList.TokenInitiative; import com.t3.model.Token; import com.t3.model.Zone; import com.t3.model.ZonePoint; import com.t3.util.guidreference.TokenReference; public class TokenPopupMenu extends AbstractTokenPopupMenu { private static final long serialVersionUID = -622385975780832588L; public TokenPopupMenu(Set<GUID> selectedTokenSet, int x, int y, ZoneRenderer renderer, Token tokenUnderMouse) { super(selectedTokenSet, x, y, renderer, tokenUnderMouse); add(new SetFacingAction()); add(new ClearFacingAction()); add(new StartMoveAction()); addOwnedItem(new ImpersonateAction()); addOwnedItem(createSizeMenu()); addOwnedItem(createMacroMenu()); addOwnedItem(createSpeechMenu()); addOwnedItem(createStateMenu()); addOwnedItem(createBarMenu()); addOwnedItem(createInitiativeMenu()); if (TabletopTool.getFrame().getInitiativePanel().hasOwnerPermission(tokenUnderMouse)) add(new ChangeInitiativeState("initiative.menu.addToInitiative")); addOwnedItem(createFlipMenu()); if (getTokenUnderMouse().getCharsheetImage() != null && AppUtil.playerOwns(getTokenUnderMouse())) { add(new ShowHandoutAction()); } add(createHaloMenu()); addOwnedItem(createArrangeMenu()); addGMItem(createChangeToMenu(Zone.Layer.GM, Zone.Layer.OBJECT, Zone.Layer.BACKGROUND)); add(new JSeparator()); /* * This adds the expose menu to token right click when the player is GM and the server setting is set to use * individual FOW */ if (TabletopTool.getPlayer().isGM() && TabletopTool.getServerPolicy().isUseIndividualFOW()) { add(createExposedFOWMenu()); } if (TabletopTool.getPlayer().isGM() || TabletopTool.getServerPolicy().getPlayersCanRevealVision()) { add(createExposeMenu()); // if (TabletopTool.getPlayer().isGM()) { // addGMItem(createVisionMenu()); // } // add(new JSeparator()); } addOwnedItem(createLightSourceMenu()); add(new JSeparator()); addToggledItem(new ShowPathsAction(), renderer.isPathShowing(tokenUnderMouse)); addToggledItem(new SnapToGridAction(tokenUnderMouse.isSnapToGrid(), renderer), tokenUnderMouse.isSnapToGrid()); addToggledGMItem(new VisibilityAction(), tokenUnderMouse.isVisible()); add(new JSeparator()); add(new JMenuItem(new CutAction())); add(new JMenuItem(new CopyAction())); add(new JMenuItem(new DeleteAction())); add(new JSeparator()); add(new RevertLastMoveAction()); add(new ShowPropertiesDialogAction()); addOwnedItem(new SaveAction()); } protected JMenu createMacroMenu() { if (selectedTokenSet.size() != 1 || getTokenUnderMouse().getMacroNames(true).size() == 0) { return null; } JMenu macroMenu = new JMenu("Macros"); List<MacroButtonProperties> macroList = getTokenUnderMouse().getMacroList(true); String group = ""; Collections.sort(macroList); Map<String, JMenu> groups = new TreeMap<String, JMenu>(); for (MacroButtonProperties macro : macroList) { group = macro.getGroup(); group = (group == null || group.isEmpty() ? " General" : group); // leading space makes it come first JMenu submenu = groups.get(group); if (submenu == null) { submenu = new JMenu(group); groups.put(group, submenu); } submenu.add(new RunMacroAction(macro.getLabel(), macro)); } // Add the group menus in alphabetical order for (JMenu submenu : groups.values()) macroMenu.add(submenu); return macroMenu; } protected JMenu createSpeechMenu() { if (selectedTokenSet.size() != 1 || getTokenUnderMouse().getSpeechNames().size() == 0) { return null; } JMenu menu = new JMenu("Speech"); List<String> keyList = new ArrayList<String>(getTokenUnderMouse().getSpeechNames()); Collections.sort(keyList); for (String key : keyList) { menu.add(new SayAction(key, getTokenUnderMouse().getSpeech(key))); } return menu; } private JMenu createExposeMenu() { JMenu menu = new JMenu("Expose"); menu.add(new ExposeVisibleAreaAction()); menu.add(new ExposeLastPathAction()); if (TabletopTool.getPlayer().getRole() == Role.GM) { menu.add(new ExposeVisibleAreaOnlyAction()); } menu.setEnabled(getTokenUnderMouse().getHasSight()); return menu; } private JMenu createExposedFOWMenu() { String viewMenu = I18N.getText("token.popup.menu.fow"); JMenu menu = new JMenu(viewMenu); //menu.add(new AddGlobalExposedAreaAction()); menu.add(new AddPartyExposedAreaAction()); Zone zone = getRenderer().getZone(); List<Token> tokens = zone.getTokens(); if (tokens != null && !tokens.isEmpty()) { String tokenViewMenu = I18N.getText("token.popup.menu.fow.tokens"); JMenu subMenu = new JMenu(tokenViewMenu); int subItemCount = 0; for (Token tok : tokens) { if (tok.getHasSight() && tok.isVisible()) { ExposedAreaMetaData meta = zone.getExposedAreaMetaData(tok.getExposedAreaGUID()); if (!meta.getExposedAreaHistory().isEmpty()) { subMenu.add(new AddTokensExposedAreaAction(tok)); subItemCount++; } } } if (subItemCount != 0) { menu.add(subMenu); } } menu.addSeparator(); menu.add(new ClearSelectedExposedAreaAction()); return menu; } private class AddTokensExposedAreaAction extends AbstractAction { private static final long serialVersionUID = 8452765509474109699L; private final TokenReference token; public AddTokensExposedAreaAction(Token token) { this.token=new TokenReference(token); String tokensView = I18N.getText("token.popup.menu.fow.tokens.view", token.getName()); I18N.setAction(tokensView, this, true); } @Override public void actionPerformed(ActionEvent e) { Zone zone = getRenderer().getZone(); Token sourceToken = token.value(); ExposedAreaMetaData sourceMeta = zone.getExposedAreaMetaData(sourceToken.getExposedAreaGUID()); for (GUID tok : selectedTokenSet) { Token targetToken = zone.getToken(tok); ExposedAreaMetaData targetMeta = zone.getExposedAreaMetaData(targetToken.getExposedAreaGUID()); targetMeta.addToExposedAreaHistory(sourceMeta.getExposedAreaHistory()); getRenderer().flush(targetToken); zone.setExposedAreaMetaData(targetToken.getExposedAreaGUID(), targetMeta); TabletopTool.serverCommand().updateExposedAreaMeta(zone.getId(), targetToken.getExposedAreaGUID(), targetMeta); } getRenderer().repaint(); } } /** * XXX If this object is supposed to merge all exposed areas together and apply that to the currently selected * tokens, why is it using a nested loop? Should one loop be used to create the exposed area object, then a second * (non-nested) loop be used to modify the exposed area of all selected tokens? */ private class AddPartyExposedAreaAction extends AbstractAction { private static final long serialVersionUID = 3672180436608883849L; public AddPartyExposedAreaAction() { I18N.setAction("token.popup.menu.fow.party", this, true); } @Override public void actionPerformed(ActionEvent e) { ZoneRenderer renderer = getRenderer(); Zone zone = renderer.getZone(); // This is ALL tokens; perhaps it should be owned tokens? Or just PC tokens? Or only those with HasSight? // Or can players not use this feature at all so the above don't matter? List<Token> allToks = zone.getTokens(); // First create an Area that includes the exposed areas of all tokens Area tokenArea = new Area(); for (Token tokenSource : allToks) { ExposedAreaMetaData sourceMeta = zone.getExposedAreaMetaData(tokenSource.getExposedAreaGUID()); tokenArea.add(sourceMeta.getExposedAreaHistory()); } // Now go back and add that Area to all selected tokens for (GUID tok : selectedTokenSet) { Token token = zone.getToken(tok); GUID tGUID = token.getExposedAreaGUID(); ExposedAreaMetaData meta = zone.getExposedAreaMetaData(tGUID); meta.addToExposedAreaHistory(tokenArea); renderer.flush(token); zone.setExposedAreaMetaData(tGUID, meta); TabletopTool.serverCommand().updateExposedAreaMeta(zone.getId(), tGUID, meta); } renderer.repaint(); } } @SuppressWarnings("unused") private class AddGlobalExposedAreaAction extends AbstractAction { private static final long serialVersionUID = -3558008167872719635L; public AddGlobalExposedAreaAction() { I18N.setAction("token.popup.menu.fow.global", this, true); } @Override public void actionPerformed(ActionEvent e) { Zone zone = getRenderer().getZone(); Area area = zone.getExposedArea(); for (GUID tok : selectedTokenSet) { Token token = zone.getToken(tok); ExposedAreaMetaData meta = zone.getExposedAreaMetaData(token.getExposedAreaGUID()); meta.addToExposedAreaHistory(area); getRenderer().flush(token); zone.setExposedAreaMetaData(token.getExposedAreaGUID(), meta); TabletopTool.serverCommand().updateExposedAreaMeta(zone.getId(), token.getExposedAreaGUID(), meta); } getRenderer().repaint(); } } private class ClearSelectedExposedAreaAction extends AbstractAction { private static final long serialVersionUID = 7969000504336361693L; public ClearSelectedExposedAreaAction() { I18N.setAction("token.popup.menu.fow.clearselected", this, true); } @Override public void actionPerformed(ActionEvent e) { if (TabletopTool.getServerPolicy().isUseIndividualFOW()) { Zone zone = getRenderer().getZone(); for (GUID tok : selectedTokenSet) { Token token = zone.getToken(tok); ExposedAreaMetaData meta = zone.getExposedAreaMetaData(token.getExposedAreaGUID()); meta.clearExposedAreaHistory(); getRenderer().flush(token); zone.setExposedAreaMetaData(token.getExposedAreaGUID(), meta); TabletopTool.serverCommand().updateExposedAreaMeta(zone.getId(), token.getExposedAreaGUID(), meta); } } getRenderer().repaint(); } } private class ExposeVisibleAreaAction extends AbstractAction { private static final long serialVersionUID = 1773049658219864418L; public ExposeVisibleAreaAction() { I18N.setAction("token.popup.menu.expose.visible", this, true); } @Override public void actionPerformed(ActionEvent e) { FogUtil.exposeVisibleArea(getRenderer(), selectedTokenSet); getRenderer().repaint(); } } private class ExposeVisibleAreaOnlyAction extends AbstractAction { private static final long serialVersionUID = 7889640443069061220L; public ExposeVisibleAreaOnlyAction() { I18N.setAction("token.popup.menu.expose.currentonly", this, true); } @Override public void actionPerformed(ActionEvent e) { FogUtil.exposePCArea(getRenderer()); TabletopTool.serverCommand().exposePCArea(getRenderer().getZone().getId()); getRenderer().repaint(); } } private class ExposeLastPathAction extends AbstractAction { private static final long serialVersionUID = 6840373835089920277L; public ExposeLastPathAction() { I18N.setAction("token.popup.menu.expose.lastpath", this, true); setEnabled(getTokenUnderMouse().getLastPath() != null); } @Override public void actionPerformed(ActionEvent e) { FogUtil.exposeLastPath(getRenderer(), selectedTokenSet); getRenderer().repaint(); } } protected JMenu createHaloMenu() { return createColorAreaMenu("token.popup.menu.halo", getTokenUnderMouse().getHaloColor(), SetHaloAction.class, SetColorChooserAction.class); } private JMenu createColorAreaMenu(String title, Color selectedColor, Class<SetHaloAction> standardColorActionClass, Class<SetColorChooserAction> customColorActionClass) { JMenu haloMenu = new JMenu(I18N.getText(title)); try { Constructor<SetHaloAction> standardColorActionConstructor = standardColorActionClass.getConstructor(new Class[] { TokenPopupMenu.class, ZoneRenderer.class, Set.class, Color.class, String.class }); Constructor<SetColorChooserAction> customColorActionConstructor = customColorActionClass.getConstructor(new Class[] { TokenPopupMenu.class, ZoneRenderer.class, Set.class, String.class }); JCheckBoxMenuItem noneMenu = new JCheckBoxMenuItem(standardColorActionConstructor.newInstance(new Object[] { this, getRenderer(), selectedTokenSet, null, "None" })); JCheckBoxMenuItem customMenu = new JCheckBoxMenuItem(customColorActionConstructor.newInstance(new Object[] { this, getRenderer(), selectedTokenSet, "Custom" })); if (selectedColor == null) { noneMenu.setSelected(true); } else { customMenu.setSelected(true); } haloMenu.add(noneMenu); haloMenu.add(customMenu); haloMenu.add(new JSeparator()); Set<String> colorNames = T3Util.getColorNames(); for (String name: colorNames) { Color bgColor = T3Util.getColor(name); Color fgColor = ColorComboBoxRenderer.selectForegroundColor(bgColor); String displayName = I18N.getString("Color.".concat(name)); if (displayName == null) { displayName = name; } JCheckBoxMenuItem item = new JCheckBoxMenuItem(standardColorActionConstructor.newInstance(new Object[] { this, getRenderer(), selectedTokenSet, bgColor, displayName })); item.setBackground(bgColor); item.setForeground(fgColor); if (bgColor.equals(selectedColor)) { item.setSelected(true); customMenu.setSelected(false); } haloMenu.add(item); } } catch (Exception e) { e.printStackTrace(); } return haloMenu; } protected JMenu createBarMenu() { List<BarTokenOverlay> overlays = new ArrayList<BarTokenOverlay>(TabletopTool.getCampaign().getTokenBarsMap().values()); if (overlays.isEmpty()) { return null; } JMenu stateMenu = I18N.createMenu("defaultTool.barMenu"); Collections.sort(overlays, BarTokenOverlay.COMPARATOR); for (BarTokenOverlay overlay : overlays) { createBarItem(overlay.getName(), stateMenu, getTokenUnderMouse()); } // endfor return stateMenu; } protected JMenu createStateMenu() { // Create the base menu JMenu stateMenu = I18N.createMenu("defaultTool.stateMenu"); stateMenu.add(new ChangeStateAction("clear")); stateMenu.addSeparator(); List<BooleanTokenOverlay> overlays = new ArrayList<BooleanTokenOverlay>(TabletopTool.getCampaign().getTokenStatesMap().values()); Collections.sort(overlays, BooleanTokenOverlay.COMPARATOR); // Create the group menus first so that they can be placed at the top of the state menu Map<String, JMenu> groups = new TreeMap<String, JMenu>(); for (BooleanTokenOverlay overlay : overlays) { String group = overlay.getGroup(); if (group != null && (group = group.trim()).length() != 0) { JMenu menu = groups.get(group); if (menu == null) { menu = new JMenu(group); groups.put(group, menu); } // endif } // endif } // endfor // Add the group menus in alphabetical order for (JMenu menu : groups.values()) stateMenu.add(menu); // Give each overlay a button in the proper menu for (BooleanTokenOverlay overlay : overlays) { String group = overlay.getGroup(); JMenu menu = stateMenu; if (group != null && (group = group.trim()).length() != 0) menu = groups.get(group); createStateItem(overlay.getName(), menu, getTokenUnderMouse()); } // endfor return stateMenu; } private JMenu createInitiativeMenu() { JMenu initiativeMenu = I18N.createMenu("initiative.menu"); boolean isOwner = TabletopTool.getFrame().getInitiativePanel().hasOwnerPermission(getTokenUnderMouse()); if (isOwner) { initiativeMenu.add(new ChangeInitiativeState("initiative.menu.add")); initiativeMenu.add(new ChangeInitiativeState("initiative.menu.remove")); initiativeMenu.addSeparator(); } // endif initiativeMenu.add(new JMenuItem(new ChangeInitiativeState("initiative.menu.resume"))); initiativeMenu.add(new JMenuItem(new ChangeInitiativeState("initiative.menu.hold"))); initiativeMenu.addSeparator(); initiativeMenu.add(new JMenuItem(new ChangeInitiativeState("initiative.menu.setState"))); initiativeMenu.add(new JMenuItem(new ChangeInitiativeState("initiative.menu.clearState"))); // Enable by state if only one token selected. if (selectedTokenSet.size() == 1) { List<Integer> list = TabletopTool.getFrame().getInitiativePanel().getList().indexOf(getTokenUnderMouse()); int index = list.isEmpty() ? -1 : list.get(0).intValue(); if (index >= 0) { if (isOwner) initiativeMenu.getMenuComponent(0).setEnabled(false); boolean hold = TabletopTool.getFrame().getInitiativePanel().getList().getTokenInitiative(index).isHolding(); if (hold) { initiativeMenu.getMenuComponent(isOwner ? 4 : 1).setEnabled(false); } else { initiativeMenu.getMenuComponent(isOwner ? 3 : 0).setEnabled(false); } } else { if (isOwner) initiativeMenu.getMenuComponent(1).setEnabled(false); initiativeMenu.getMenuComponent(isOwner ? 4 : 3).setEnabled(false); initiativeMenu.getMenuComponent(isOwner ? 3 : 0).setEnabled(false); } // endif } // endif return initiativeMenu; } protected void addOwnedToggledItem(Action action, boolean checked) { if (action == null) { return; } JCheckBoxMenuItem item = new JCheckBoxMenuItem(action); item.setSelected(checked); item.setEnabled(tokensAreOwned()); add(item); } @Override public void showPopup(JComponent component) { show(component, x, y); } @SuppressWarnings("unused") private static class PlayerOwnershipMenu extends JCheckBoxMenuItem implements ActionListener { private static final long serialVersionUID = -6109869878632628827L; private final Set<GUID> tokenSet; private final Zone zone; private final boolean selected; private final String name; public PlayerOwnershipMenu(String name, boolean selected, Set<GUID> tokenSet, Zone zone) { super(name, selected); this.tokenSet = tokenSet; this.zone = zone; this.selected = selected; this.name = name; addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { for (GUID guid : tokenSet) { Token token = zone.getToken(guid); if (selected) { for (Player player : TabletopTool.getPlayerList()) { token.addOwner(player.getName()); } token.removeOwner(name); } else { token.addOwner(name); } TabletopTool.serverCommand().putToken(zone.getId(), token); } TabletopTool.getFrame().updateTokenTree(); } } /** * Create a radio button menu item for a particuar state * * @param state * Create the item for this state * @param menu * The menu containing all items. * @return A menu item for the passed state. */ private JCheckBoxMenuItem createStateItem(String state, JMenu menu, Token token) { JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ChangeStateAction(state)); item.setSelected(token.hasState(state)); menu.add(item); return item; } private JMenuItem createBarItem(String bar, JMenu menu, Token token) { JMenuItem item = new JMenuItem(new ChangeBarAction(bar)); Float value = token.getBar(bar); if(value==null) item.setText(bar + " (hidden)"); else item.setText(bar + " (" + (int) (value * 100) + "%)"); menu.add(item); return item; } private class SetHaloAction extends AbstractAction { private static final long serialVersionUID = 936075111485618012L; protected Color color; protected Set<GUID> tokenSet; protected ZoneRenderer renderer; public SetHaloAction(ZoneRenderer renderer, Set<GUID> tokenSet, Color color, String name) { this.color = color; this.tokenSet = tokenSet; this.renderer = renderer; putValue(Action.NAME, name); } @Override public void actionPerformed(ActionEvent e) { Zone zone = renderer.getZone(); for (GUID guid : tokenSet) { Token token = zone.getToken(guid); if (!AppUtil.playerOwns(token)) { continue; } updateToken(token, color); TabletopTool.serverCommand().putToken(zone.getId(), token); } TabletopTool.getFrame().updateTokenTree(); renderer.repaint(); } protected void updateToken(Token token, Color color) { token.setHaloColor(color); } } private class SetColorChooserAction extends AbstractAction { private static final long serialVersionUID = 2212977067043864272L; protected Color currentColor; protected Set<GUID> tokenSet; protected ZoneRenderer renderer; // private final String title = "Choose Halo Color"; public SetColorChooserAction(ZoneRenderer renderer, Set<GUID> tokenSet, String name) { this.tokenSet = tokenSet; this.renderer = renderer; this.currentColor = renderer.getZone().getToken(tokenSet.iterator().next()).getHaloColor(); putValue(Action.NAME, name); } @Override public void actionPerformed(ActionEvent e) { Color color = showColorChooserDialog(); if (color != null) { Zone zone = renderer.getZone(); for (GUID guid : tokenSet) { Token token = zone.getToken(guid); if (!AppUtil.playerOwns(token)) { continue; } updateToken(token, color); TabletopTool.serverCommand().putToken(zone.getId(), token); } TabletopTool.getFrame().updateTokenTree(); renderer.repaint(); } } protected Color showColorChooserDialog() { return JColorChooser.showDialog(TabletopTool.getFrame().getContentPane(), "Choose Halo Color", currentColor); } protected void updateToken(Token token, Color color) { token.setHaloColor(color); } } @SuppressWarnings("unused") private class SetVisionOverlayColorChooserAction extends SetColorChooserAction { private static final long serialVersionUID = 5809668032069953020L; public SetVisionOverlayColorChooserAction(ZoneRenderer renderer, Set<GUID> tokenSet, String name) { super(renderer, tokenSet, name); this.currentColor = renderer.getZone().getToken(tokenSet.iterator().next()).getVisionOverlayColor(); } @Override protected Color showColorChooserDialog() { return JColorChooser.showDialog(TabletopTool.getFrame().getContentPane(), "Choose Vision Overlay Color", currentColor); } @Override protected void updateToken(Token token, Color color) { token.setVisionOverlayColor(color); } } @SuppressWarnings("unused") private class SetVisionOverlayColorAction extends SetHaloAction { private static final long serialVersionUID = 5116100872119403176L; public SetVisionOverlayColorAction(ZoneRenderer renderer, Set<GUID> tokenSet, Color color, String name) { super(renderer, tokenSet, color, name); } @Override protected void updateToken(Token token, Color color) { token.setVisionOverlayColor(color); } } private class ChangeBarAction extends AbstractAction { private static final long serialVersionUID = 3992963841229973540L; public ChangeBarAction(String bar) { putValue(ACTION_COMMAND_KEY, bar); putValue(NAME, bar); } @Override public void actionPerformed(ActionEvent e) { String name = (String) getValue(NAME); JSlider slider = new JSlider(0, 100); JPanel labelPanel = new JPanel(new FormLayout("pref", "pref 2px:grow pref")); labelPanel.add(new JLabel(name + ":"), new CellConstraints(1, 1, CellConstraints.RIGHT, CellConstraints.TOP)); JCheckBox hide = new JCheckBox("Hide"); hide.putClientProperty("JSlider", slider); hide.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { JSlider js = (JSlider) ((JCheckBox) e.getSource()).getClientProperty("JSlider"); js.setEnabled(!((JCheckBox) e.getSource()).isSelected()); } }); labelPanel.add(hide, new CellConstraints(1, 3, CellConstraints.RIGHT, CellConstraints.TOP)); slider.setPaintLabels(true); slider.setPaintTicks(true); slider.setMajorTickSpacing(20); slider.createStandardLabels(20); slider.setMajorTickSpacing(10); if (getTokenUnderMouse().getBar(name) == null) { hide.setSelected(true); slider.setEnabled(false); slider.setValue(100); } else { hide.setSelected(false); slider.setEnabled(true); slider.setValue((int) (getTokenUnderMouse().getBar(name) * 100)); } JPanel barPanel = new JPanel(new FormLayout("right:pref 2px pref", "pref")); barPanel.add(labelPanel, new CellConstraints(1, 1)); barPanel.add(slider, new CellConstraints(3, 1)); if (JOptionPane.showOptionDialog(TabletopTool.getFrame(), barPanel, "Set " + name + " Value", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, null, null) == JOptionPane.OK_OPTION) { Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone(); for (GUID tokenGUID : selectedTokenSet) { Token token = zone.getToken(tokenGUID); Float val = hide.isSelected() ? null : new Float(slider.getValue() / 100.0); token.setBar(name, val); TabletopTool.serverCommand().putToken(zone.getId(), token); } } } } /** * Internal class used to handle token state changes. */ private class ChangeStateAction extends AbstractAction { private static final long serialVersionUID = 8403066587828844564L; /** * Initialize a state action for a given state. * * @param state * The name of the state set when this action is executed */ public ChangeStateAction(String state) { putValue(ACTION_COMMAND_KEY, state); // Set the state command // Load the name, mnemonic, accelerator, and description if // available String key = "defaultTool.stateAction." + state; String name = com.t3.language.I18N.getText(key); if (!name.equals(key)) { putValue(NAME, name); int mnemonic = I18N.getMnemonic(key); if (mnemonic != -1) putValue(MNEMONIC_KEY, mnemonic); String accel = I18N.getAccelerator(key); if (accel != null) putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(accel)); String description = I18N.getDescription(key); if (description != null) putValue(SHORT_DESCRIPTION, description); } else { // Default name if no I18N set putValue(NAME, state); } // endif } /** * Set the state for all of the selected tokens. * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent aE) { ZoneRenderer renderer = TabletopTool.getFrame().getCurrentZoneRenderer(); for (GUID tokenGUID : selectedTokenSet) { Token token = renderer.getZone().getToken(tokenGUID); if (aE.getActionCommand().equals("clear")) { for (String state : TabletopTool.getCampaign().getTokenStatesMap().keySet()) token.setState(state, false); } else { token.setState(aE.getActionCommand(), ((JCheckBoxMenuItem) aE.getSource()).isSelected()); } // endif TabletopTool.serverCommand().putToken(renderer.getZone().getId(), token); } // endfor renderer.repaint(); } } private class ChangeInitiativeState extends AbstractAction { private static final long serialVersionUID = -5968571073361988758L; String name; public ChangeInitiativeState(String aName) { name = aName; I18N.setAction(aName, this); } @Override public void actionPerformed(ActionEvent e) { Zone zone = getRenderer().getZone(); InitiativeList init = TabletopTool.getFrame().getInitiativePanel().getList(); String input = null; if (name.equals("initiative.menu.setState")) { input = JOptionPane.showInputDialog(I18N.getText("initiative.menu.enterState")); if (input == null) return; input = input.trim(); } // endif for (GUID id : selectedTokenSet) { Token token = zone.getToken(id); Integer[] list = init.indexOf(token).toArray(new Integer[0]); if (name.equals("initiative.menu.add") || name.equals("initiative.menu.addToInitiative")) { init.insertToken(-1, token); } else { for (int i = list.length - 1; i >= 0; i--) { int index = list[i].intValue(); if (name.equals("initiative.menu.remove")) { if (index != -1) init.removeToken(index); } else if (name.equals("initiative.menu.hold")) { if (index != -1) init.getTokenInitiative(index).setHolding(true); } else if (name.equals("initiative.menu.resume")) { if (index != -1) init.getTokenInitiative(index).setHolding(false); } else if (name.equals("initiative.menu.setState")) { if (index != -1) init.getTokenInitiative(index).setUnparsedState(input); } else if (name.equals("initiative.menu.clearState")) { if (index != -1) init.getTokenInitiative(index).setState((InitiativeValue)null); } // endif } // endif } // endfor } // endfor } } @SuppressWarnings("unused") private class AllOwnershipAction extends AbstractAction { private static final long serialVersionUID = -2995489619896660807L; @Override public void actionPerformed(ActionEvent e) { Zone zone = getRenderer().getZone(); for (GUID tokenGUID : selectedTokenSet) { Token token = zone.getToken(tokenGUID); if (token != null) { token.setOwnedByAll(true); TabletopTool.serverCommand().putToken(zone.getId(), token); } } } } @SuppressWarnings("unused") private class RemoveAllOwnershipAction extends AbstractAction { private static final long serialVersionUID = -6767778461889310579L; @Override public void actionPerformed(ActionEvent e) { Zone zone = getRenderer().getZone(); for (GUID tokenGUID : selectedTokenSet) { Token token = zone.getToken(tokenGUID); if (token != null) { token.clearAllOwners(); TabletopTool.serverCommand().putToken(zone.getId(), token); } } } } private class ShowPathsAction extends AbstractAction { private static final long serialVersionUID = 5704307506738212375L; public ShowPathsAction() { putValue(Action.NAME, "Show Path"); } @Override public void actionPerformed(ActionEvent e) { for (GUID tokenGUID : selectedTokenSet) { Token token = getRenderer().getZone().getToken(tokenGUID); if (token == null) { continue; } getRenderer().showPath(token, !getRenderer().isPathShowing(getTokenUnderMouse())); } getRenderer().repaint(); } } private class RevertLastMoveAction extends AbstractAction { private static final long serialVersionUID = 8967703198797674025L; public RevertLastMoveAction() { putValue(Action.NAME, "Revert Last Move"); // Only available if there is a last move for (GUID tokenGUID : selectedTokenSet) { Token token = getRenderer().getZone().getToken(tokenGUID); if (token == null) { continue; } if (token.getLastPath() == null) { setEnabled(false); break; } } } @Override public void actionPerformed(ActionEvent e) { Zone zone = getRenderer().getZone(); for (GUID tokenGUID : selectedTokenSet) { Token token = zone.getToken(tokenGUID); if (token == null) { continue; } Path<?> path = token.getLastPath(); if (path == null) { continue; } // Get the start cell of the last move // TODO: I don't like this hard wiring, find a better way ZonePoint zp = null; if (path.getCellPath().get(0) instanceof CellPoint) { zp = zone.getGrid().convert((CellPoint) path.getCellPath().get(0)); } else { zp = (ZonePoint) path.getCellPath().get(0); } // Relocate token.setX(zp.x); token.setY(zp.y); // Do it again to cancel out the last move position token.setX(zp.x); token.setY(zp.y); // No more last path token.setLastPath(null); TabletopTool.serverCommand().putToken(zone.getId(), token); // Cache clearing getRenderer().flush(token); } getRenderer().repaint(); } } public class RunMacroAction extends AbstractAction { private static final long serialVersionUID = -5836981653612993828L; private final MacroButtonProperties macro; public RunMacroAction(String key, MacroButtonProperties macro) { putValue(Action.NAME, key); this.macro = macro; } @Override public void actionPerformed(ActionEvent e) { Set<Token> guidSet = new HashSet<Token>(); for (GUID tokenID : selectedTokenSet) { guidSet.add(getRenderer().getZone().getToken(tokenID)); } macro.executeMacro(guidSet); } } public class SayAction extends AbstractAction { private static final long serialVersionUID = -4560161692286043464L; private final String speech; public SayAction(String key, String speech) { putValue(Action.NAME, key); this.speech = speech; } @Override public void actionPerformed(ActionEvent e) { String identity = getTokenUnderMouse().getName(); String command = "/im " + identity + ":" + speech; JTextComponent commandArea = TabletopTool.getFrame().getCommandPanel().getCommandTextArea(); commandArea.setText(command); TabletopTool.getFrame().getCommandPanel().commitCommand(); } } }