/* * 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.tokenpanel; import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.InputMap; import javax.swing.JCheckBoxMenuItem; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import com.jeta.forms.components.line.HorizontalLineComponent; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jidesoft.swing.JideButton; import com.jidesoft.swing.JideSplitButton; import com.t3.client.AppPreferences; import com.t3.client.AppUtil; import com.t3.client.TabletopTool; import com.t3.client.ui.zone.ZoneRenderer; import com.t3.guid.GUID; import com.t3.language.I18N; import com.t3.model.ModelChangeEvent; import com.t3.model.ModelChangeListener; import com.t3.model.Token; import com.t3.model.Token.Type; import com.t3.model.Zone; import com.t3.model.Zone.Event; import com.t3.model.chat.TextMessage; import com.t3.model.initiative.InitiativeList; import com.t3.model.initiative.InitiativeListModel; import com.t3.model.initiative.InitiativeList.TokenInitiative; /** * This panel shows the initiative order inside of TabletopTools. * * @author Jay */ public class InitiativePanel extends JPanel implements PropertyChangeListener, ModelChangeListener, ListSelectionListener { /*--------------------------------------------------------------------------------------------- * Instance Variables *-------------------------------------------------------------------------------------------*/ /** * Model containing all of the tokens in this initiative. */ private InitiativeList list; /** * The model used to display a list in the panel; */ private final InitiativeListModel model; /** * Component that displays the round */ private final JLabel round; /** * Component that displays the initiative list. */ private final JList displayList; /** * Flag indicating that token images are shown in the list. */ private boolean showTokens = AppPreferences.getInitShowTokens(); /** * Flag indicating that token states are shown in the list. Only valid if {@link #showTokens} is <code>true</code>. */ private boolean showTokenStates = AppPreferences.getInitShowTokenStates(); /** * Flag indicating that initiative state is shown in the list. */ private boolean showInitState = AppPreferences.getInitShowInitiative(); /** * Flag indicating that two lines are used for initiative stated. It is only valid if {@link #showInitState} is * <code>true</code>. */ private boolean initStateSecondLine = AppPreferences.getInitShow2ndLine(); /** * The zone data being displayed. */ private Zone zone; /** * The component that contains the initiative menu. */ private final JideSplitButton menuButton; /** * The menu item that tells the GM if NPC's are visible. */ private JCheckBoxMenuItem hideNPCMenuItem; /** * The menu item that tells the GM if players can change the initiative when working with tokens they own. */ private JCheckBoxMenuItem ownerPermissionsMenuItem; /** * The menu item that tells the GM if players can only move their tokens when it is their turn. */ private JCheckBoxMenuItem movementLockMenuItem; /** * Flag indicating that the owners of tokens have been granted permission to restricted actions when they own the * token. */ private boolean ownerPermissions; /** * Flag indicating that the owners of tokens can only move their tokens when it is their turn. */ private boolean movementLock; /*--------------------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------------------*/ /** * Setup the menu */ public InitiativePanel() { // Build the form and add it's component setLayout(new BorderLayout()); JPanel panel = new JPanel(new FormLayout("2px pref 8dlu pref 4dlu fill:30px 0px:grow 2px", "4dlu fill:pref 7px fill:0px:grow 4dlu")); add(panel, SwingConstants.CENTER); menuButton = new JideSplitButton(I18N.getText("initPanel.menuButton")); panel.add(menuButton, new CellConstraints(2, 2)); JideButton rButton = new JideButton(RESET_COUNTER_ACTION); rButton.setButtonStyle(JideButton.TOOLBOX_STYLE); panel.add(rButton, new CellConstraints(4, 2)); round = new JLabel(); round.setHorizontalAlignment(SwingConstants.CENTER); round.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); round.setFont(getFont().deriveFont(Font.BOLD)); panel.add(round, new CellConstraints(6, 2)); panel.add(new HorizontalLineComponent(), new CellConstraints(2, 3, 6, 1)); ownerPermissions = TabletopTool.getCampaign().isInitiativeOwnerPermissions(); movementLock = TabletopTool.getCampaign().isInitiativeMovementLock(); // Set up the list with an empty model displayList = new JList(); model = new InitiativeListModel(); displayList.setModel(model); setList(new InitiativeList(null)); displayList.setCellRenderer(new InitiativeListCellRenderer(this)); // Dragging is only for GM displayList.setTransferHandler(new InitiativeTransferHandler(this)); displayList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); displayList.addListSelectionListener(this); displayList.addMouseListener(new MouseHandler()); panel.add(new JScrollPane(displayList), new CellConstraints(2, 4, 6, 1)); // Set the keyboard mapping InputMap imap = displayList.getInputMap(); imap.put(KeyStroke.getKeyStroke("DELETE"), "REMOVE_TOKEN_ACTION"); ActionMap map = displayList.getActionMap(); map.put("REMOVE_TOKEN_ACTION", REMOVE_TOKEN_ACTION); // Set action text I18N.setAction("initPanel.sort", SORT_LIST_ACTION); I18N.setAction("initPanel.toggleHold", TOGGLE_HOLD_ACTION); I18N.setAction("initPanel.makeCurrent", MAKE_CURRENT_ACTION); I18N.setAction("initPanel.setInitState", SET_INIT_STATE_VALUE); I18N.setAction("initPanel.clearInitState", CLEAR_INIT_STATE_VALUE); I18N.setAction("initPanel.showTokens", SHOW_TOKENS_ACTION); I18N.setAction("initPanel.showTokenStates", SHOW_TOKEN_STATES_ACTION); I18N.setAction("initPanel.showInitStates", SHOW_INIT_STATE); I18N.setAction("initPanel.initStateSecondLine", INIT_STATE_SECOND_LINE); I18N.setAction("initPanel.toggleHideNPCs", TOGGLE_HIDE_NPC_ACTION); I18N.setAction("initPanel.addPCs", ADD_PCS_ACTION); I18N.setAction("initPanel.addAll", ADD_ALL_ACTION); I18N.setAction("initPanel.remove", REMOVE_TOKEN_ACTION); I18N.setAction("initPanel.removeAll", REMOVE_ALL_ACTION); I18N.setAction("initPanel.remove", REMOVE_TOKEN_ACTION); I18N.setAction("initPanel.menuButton", NEXT_ACTION); I18N.setAction("initPanel.toggleOwnerPermissions", TOGGLE_OWNER_PERMISSIONS_ACTION); I18N.setAction("initPanel.toggleMovementLock", TOGGLE_MOVEMENT_LOCK_ACTION); I18N.setAction("initPanel.round", RESET_COUNTER_ACTION); I18N.setAction("initPanel.prev", PREV_ACTION); updateView(); } /*--------------------------------------------------------------------------------------------- * Instance Methods *-------------------------------------------------------------------------------------------*/ /** * Update the view after the connection has been created. This allows the menus to be tailored for GM's and Player's * properly */ public void updateView() { displayList.setDragEnabled(hasGMPermission()); // Set up the button if (ownerPermissions || hasGMPermission()) { NEXT_ACTION.setEnabled(true); menuButton.setAction(NEXT_ACTION); } else { TOGGLE_HOLD_ACTION.setEnabled(true); menuButton.setAction(TOGGLE_HOLD_ACTION); } // endif // Set up the menu menuButton.removeAll(); if (hasGMPermission()) { menuButton.add(new JMenuItem(PREV_ACTION)); menuButton.add(new JMenuItem(SORT_LIST_ACTION)); menuButton.addSeparator(); menuButton.add(new JMenuItem(MAKE_CURRENT_ACTION)); } // endif if (ownerPermissions || hasGMPermission()) { menuButton.add(new JMenuItem(TOGGLE_HOLD_ACTION)); } // endif menuButton.add(new JMenuItem(SET_INIT_STATE_VALUE)); menuButton.add(new JMenuItem(CLEAR_INIT_STATE_VALUE)); menuButton.addSeparator(); JCheckBoxMenuItem item = new JCheckBoxMenuItem(SHOW_TOKENS_ACTION); item.setSelected(showTokens); menuButton.add(item); item = new JCheckBoxMenuItem(SHOW_TOKEN_STATES_ACTION); item.setSelected(showTokenStates); menuButton.add(item); item = new JCheckBoxMenuItem(SHOW_INIT_STATE); item.setSelected(showInitState); menuButton.add(item); item = new JCheckBoxMenuItem(INIT_STATE_SECOND_LINE); item.setSelected(initStateSecondLine); menuButton.add(item); if (hasGMPermission()) { hideNPCMenuItem = new JCheckBoxMenuItem(TOGGLE_HIDE_NPC_ACTION); hideNPCMenuItem.setSelected(list == null ? false : list.isHideNPC()); menuButton.add(hideNPCMenuItem); ownerPermissionsMenuItem = new JCheckBoxMenuItem(TOGGLE_OWNER_PERMISSIONS_ACTION); ownerPermissionsMenuItem.setSelected(list == null ? false : ownerPermissions); menuButton.add(ownerPermissionsMenuItem); movementLockMenuItem = new JCheckBoxMenuItem(TOGGLE_MOVEMENT_LOCK_ACTION); movementLockMenuItem.setSelected(list == null ? false : movementLock); menuButton.add(movementLockMenuItem); menuButton.addSeparator(); menuButton.add(new JMenuItem(ADD_PCS_ACTION)); menuButton.add(new JMenuItem(ADD_ALL_ACTION)); menuButton.addSeparator(); menuButton.add(new JMenuItem(REMOVE_TOKEN_ACTION)); menuButton.add(new JMenuItem(REMOVE_ALL_ACTION)); menuButton.setText(I18N.getText("initPanel.menuButton")); } else if (ownerPermissions) { menuButton.addSeparator(); menuButton.add(new JMenuItem(REMOVE_TOKEN_ACTION)); } // endif valueChanged(null); } /** * Remove all of the tokens from the model and clear round and current */ public void clearTokens() { list.clearModel(); } /** * Make sure that the token references match the zone */ public void update() { list.update(); } /** @return Getter for list */ public InitiativeList getList() { return list; } /** * @param theList * Setter for the list to set */ public void setList(InitiativeList theList) { // Remove the old list if (list == theList) return; if (list != null) list.removePropertyChangeListener(this); // Add the new one list = theList; if (list != null) { list.addPropertyChangeListener(this); round.setText(list.getRound() >= 0 ? Integer.toString(list.getRound()) : ""); } EventQueue.invokeLater(new Runnable() { @Override public void run() { model.setList(list); if (menuButton != null && menuButton.getAction() == NEXT_ACTION) menuButton.setButtonEnabled(hasGMPermission() || list.getCurrent() >= 0 && hasOwnerPermission(list.getToken(list.getCurrent()))); if (list.getCurrent() >= 0) { int index = model.getDisplayIndex(list.getCurrent()); if (index >= 0) displayList.ensureIndexIsVisible(index); } } }); } /** @return Getter for showTokens */ public boolean isShowTokens() { return showTokens; } /** @return Getter for showTokenStates */ public boolean isShowTokenStates() { return showTokenStates; } /** @return Getter for showInitState */ public boolean isShowInitState() { return showInitState; } /** @return Getter for model */ public InitiativeListModel getModel() { return model; } /** * Set the zone that we are currently working on. * * @param aZone * The new zone */ public void setZone(Zone aZone) { // Clean up listeners if (aZone == zone) return; if (zone != null) zone.removeModelChangeListener(this); zone = aZone; if (zone != null) zone.addModelChangeListener(this); // Older campaigns didn't have a list, make sure this one does InitiativeList list = (zone != null) ? zone.getInitiativeList() : new InitiativeList(null); if (list == null) { list = new InitiativeList(zone); zone.setInitiativeList(list); } // endif // Set the list and actions setList(list); displayList.getSelectionModel().clearSelection(); updateView(); } /** * See if the current player has permission to execute owner restricted actions. * * @param token * Check this token's ownership. If this value is <code>null</code> then <code>false</code> is returned. * @return The value <code>true</code> if this player has permission for restricted actions. */ public boolean hasOwnerPermission(Token token) { if (token == null) return false; if (hasGMPermission()) return true; if (ownerPermissions && (!TabletopTool.getServerPolicy().useStrictTokenManagement() || token.isOwner(TabletopTool.getPlayer().getName()))) return true; return false; } /** * See if the current player has permission to execute GM restricted actions. This is <b>not</b> related to * so-called <i>trusted macros</i> in MTscript. * * @return The value <code>true</code> if this player has permission for all actions. */ public boolean hasGMPermission() { return (TabletopTool.getPlayer() == null || TabletopTool.getPlayer().isGM()); } /** @return Getter for ownerPermissions */ public boolean isOwnerPermissions() { return ownerPermissions; } /** * @param anOwnerPermissions * Setter for ownerPermissions */ public void setOwnerPermissions(boolean anOwnerPermissions) { ownerPermissions = anOwnerPermissions; updateView(); } /** @return Getter for MovementLock */ public boolean isMovementLock() { return movementLock; } /** * @param anMovementLock * Setter for MovementLock */ public void setMovementLock(boolean anMovementLock) { movementLock = anMovementLock; } /** * Returns true if the passed token can not be moved because it is not the current token. * * @param token * The token being checked. * @return <code>true</code> if token movement is locked. */ public boolean isMovementLocked(Token token) { if (!movementLock || list == null || list.getSize() == 0) return false; if (model.getCurrentTokenInitiative() == null) return true; if (model.getCurrentTokenInitiative().getToken() == token) return false; return true; } /** @return Getter for initStateSecondLine */ public boolean isInitStateSecondLine() { return initStateSecondLine; } /** * @param initStateSecondLine * Setter for initStateSecondLine */ public void setInitStateSecondLine(boolean initStateSecondLine) { this.initStateSecondLine = initStateSecondLine; } /*--------------------------------------------------------------------------------------------- * ListSelectionListener Interface Methods *-------------------------------------------------------------------------------------------*/ /** * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent) */ @Override public void valueChanged(ListSelectionEvent e) { if (e != null && e.getValueIsAdjusting()) return; TokenInitiative ti = (TokenInitiative) displayList.getSelectedValue(); boolean enabled = (ti != null && hasOwnerPermission(ti.getToken())) ? true : false; CLEAR_INIT_STATE_VALUE.setEnabled(enabled); SET_INIT_STATE_VALUE.setEnabled(enabled); if (menuButton.getAction() == TOGGLE_HOLD_ACTION) { menuButton.setButtonEnabled(enabled); } else { TOGGLE_HOLD_ACTION.setEnabled(enabled); } // endif MAKE_CURRENT_ACTION.setEnabled(enabled && ti != list.getTokenInitiative(list.getCurrent())); REMOVE_TOKEN_ACTION.setEnabled(enabled); ti = (list.getCurrent() >= 0) ? list.getTokenInitiative(list.getCurrent()) : null; if (hasGMPermission() || (ti != null && hasOwnerPermission(ti.getToken()))) { menuButton.setButtonEnabled(true); } else { if (menuButton.getAction() == NEXT_ACTION) menuButton.setButtonEnabled(false); } } /*--------------------------------------------------------------------------------------------- * PropertyChangeListener Interface Methods *-------------------------------------------------------------------------------------------*/ /** * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(InitiativeList.ROUND_PROP)) { String text = list.getRound() < 0 ? "" : Integer.toString(list.getRound()); round.setText(text); } else if (evt.getPropertyName().equals(InitiativeList.CURRENT_PROP)) { if (list.getCurrent() < 0) return; Token t = list.getTokenInitiative(list.getCurrent()).getToken(); if (t == null) return; String s = I18N.getText("initPanel.displayMessage", t.getName()); if (InitiativeListModel.isTokenVisible(t, list.isHideNPC()) && t.getType() != Type.NPC && AppPreferences.isShowInitGainMessage()) TabletopTool.addMessage(TextMessage.say(s)); displayList.ensureIndexIsVisible(model.getDisplayIndex(list.getCurrent())); if (menuButton.getAction() == NEXT_ACTION) menuButton.setButtonEnabled(list.getCurrent() >= 0 && hasOwnerPermission(list.getToken(list.getCurrent()))); } else if (evt.getPropertyName().equals(InitiativeList.TOKENS_PROP)) { if ((evt.getOldValue() == null && evt.getNewValue() instanceof TokenInitiative) || (evt.getNewValue() == null & evt.getOldValue() instanceof TokenInitiative)) displayList.getSelectionModel().clearSelection(); } else if (evt.getPropertyName().equals(InitiativeList.HIDE_NPCS_PROP)) { displayList.getSelectionModel().clearSelection(); } else if (evt.getPropertyName().equals(InitiativeList.OWNER_PERMISSIONS_PROP)) { updateView(); } // endif } /*--------------------------------------------------------------------------------------------- * ModelChangeListener Interface Methods *-------------------------------------------------------------------------------------------*/ /** * @see com.t3.model.ModelChangeListener#modelChanged(com.t3.model.ModelChangeEvent) */ @Override public void modelChanged(ModelChangeEvent event) { if (event.getEvent().equals(Event.INITIATIVE_LIST_CHANGED)) { if ((Zone) event.getModel() == zone) { int oldSize = model.getSize(); setList(((Zone) event.getModel()).getInitiativeList()); if (oldSize != model.getSize()) displayList.getSelectionModel().clearSelection(); } } else if (event.getEvent().equals(Event.TOKEN_ADDED) || event.getEvent().equals(Event.TOKEN_CHANGED) || event.getEvent().equals(Event.TOKEN_REMOVED)) { model.updateModel(); } } /*--------------------------------------------------------------------------------------------- * Menu Actions *-------------------------------------------------------------------------------------------*/ /** * This action will advance initiative to the next token in the list. */ public final Action NEXT_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { list.nextInitiative(); }; }; /** * This action will reverse initiative to the previous token in the list. */ public final Action PREV_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { list.prevInitiative(); }; }; /** * This action will remove the selected token from the list. */ public final Action REMOVE_TOKEN_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { TokenInitiative ti = (TokenInitiative) displayList.getSelectedValue(); if (ti == null) return; int index = list.indexOf(ti); list.removeToken(index); }; }; /** * This action will turn the selected token's initiative on and off. */ public final Action TOGGLE_HOLD_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { TokenInitiative ti = (TokenInitiative) displayList.getSelectedValue(); if (ti == null) return; ti.setHolding(!ti.isHolding()); }; }; /** * This action will make the selected token the current token. */ public final Action MAKE_CURRENT_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { TokenInitiative ti = (TokenInitiative) displayList.getSelectedValue(); if (ti == null) return; list.setCurrent(list.indexOf(ti)); }; }; /** * This action toggles the display of token images. */ public final Action SHOW_TOKENS_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { showTokens = ((JCheckBoxMenuItem) e.getSource()).isSelected(); displayList.setCellRenderer(new InitiativeListCellRenderer(InitiativePanel.this)); // Regenerates the size of each row. AppPreferences.setInitShowTokens(showTokens); }; }; /** * This action toggles the display of token images. */ public final Action SHOW_TOKEN_STATES_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { showTokenStates = ((JCheckBoxMenuItem) e.getSource()).isSelected(); displayList.setCellRenderer(new InitiativeListCellRenderer(InitiativePanel.this)); // Regenerates the size of each row. AppPreferences.setInitShowTokenStates(showTokenStates); }; }; /** * This action toggles the display of token images. */ public final Action SHOW_INIT_STATE = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { showInitState = ((JCheckBoxMenuItem) e.getSource()).isSelected(); displayList.setCellRenderer(new InitiativeListCellRenderer(InitiativePanel.this)); // Regenerates the size of each row. AppPreferences.setInitShowInitiative(showInitState); }; }; /** * This action toggles the display of token images. */ public final Action INIT_STATE_SECOND_LINE = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { initStateSecondLine = ((JCheckBoxMenuItem) e.getSource()).isSelected(); displayList.setCellRenderer(new InitiativeListCellRenderer(InitiativePanel.this)); // Regenerates the size of each row. AppPreferences.setInitShow2ndLine(initStateSecondLine); }; }; /** * This action sorts the tokens in the list. */ public final Action SORT_LIST_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { list.sort(); }; }; /** * This action will set the initiative state of the currently selected token. */ public final Action SET_INIT_STATE_VALUE = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { TokenInitiative ti = (TokenInitiative) displayList.getSelectedValue(); if (ti == null) return; Token token = ti.getToken(); String sName = (token == null) ? "" : token.getName(); if (hasGMPermission() && token != null && token.getGMName() != null && token.getGMName().trim().length() != 0) sName += " (" + token.getGMName().trim() + ")"; String s = I18N.getText("initPanel.enterState", sName); String input = JOptionPane.showInputDialog(s, ti.getState()); if (input == null) return; ti.setUnparsedState(input.trim()); }; }; /** * This action will clear the initiative state of the currently selected token. */ public final Action CLEAR_INIT_STATE_VALUE = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { TokenInitiative ti = (TokenInitiative) displayList.getSelectedValue(); if (ti == null) return; ti.setState((String)null); }; }; /** * This action will remove all tokens from the initiative panel. */ public final Action REMOVE_ALL_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { clearTokens(); }; }; /** * This action will add all tokens in the zone to this initiative panel. */ public final Action ADD_ALL_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { list.insertTokens(list.getZone().getTokens()); }; }; /** * This action will add all PC tokens in the zone to this initiative panel. */ public final Action ADD_PCS_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { List<Token> tokens = new ArrayList<Token>(); for (Token token : list.getZone().getTokens()) { if (token.getType() == Type.PC) tokens.add(token); } // endfor list.insertTokens(tokens); }; }; /** * This action will hide all initiative items with NPC tokens from players */ public final Action TOGGLE_HIDE_NPC_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { list.setHideNPC(!list.isHideNPC()); if (list.isHideNPC() != hideNPCMenuItem.isSelected()) hideNPCMenuItem.setSelected(list.isHideNPC()); }; }; /** * This action will toggle the flag that allows players to modify the init for tokens they own. */ public final Action TOGGLE_OWNER_PERMISSIONS_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { boolean op = !TabletopTool.getCampaign().isInitiativeOwnerPermissions(); if (ownerPermissionsMenuItem != null) ownerPermissionsMenuItem.setSelected(op); TabletopTool.getCampaign().setInitiativeOwnerPermissions(op); TabletopTool.serverCommand().updateCampaign(TabletopTool.getCampaign().getCampaignProperties()); }; }; /** * This action will toggle the flag that allows players to only move tokens when it is their turn. */ public final Action TOGGLE_MOVEMENT_LOCK_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { boolean op = !TabletopTool.getCampaign().isInitiativeMovementLock(); if (ownerPermissionsMenuItem != null) ownerPermissionsMenuItem.setSelected(op); TabletopTool.getCampaign().setInitiativeMovementLock(op); TabletopTool.serverCommand().updateCampaign(TabletopTool.getCampaign().getCampaignProperties()); }; }; /** * This action will reset the round counter for the initiative panel. */ public final Action RESET_COUNTER_ACTION = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (!TabletopTool.getPlayer().isGM()) { return; } list.startUnitOfWork(); list.setRound(-1); list.setCurrent(-1); list.finishUnitOfWork(); }; }; /*--------------------------------------------------------------------------------------------- * DoubleClickHandler Inner Class *-------------------------------------------------------------------------------------------*/ /** * Handle a double click and context menu on the list of the table. * * @author jgorrell * @version $Revision$ $Date$ $Author$ */ private class MouseHandler extends MouseAdapter { /** * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) */ @Override public void mouseClicked(MouseEvent e) { if (model.getSize() == 0) { return; } if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (displayList.getSelectedValue() != null) { // Show the selected token on the map. Token token = ((TokenInitiative) displayList.getSelectedValue()).getToken(); ZoneRenderer renderer = TabletopTool.getFrame().getCurrentZoneRenderer(); if (renderer == null || token == null || (!token.isToken() && !TabletopTool.getPlayer().isGM()) || !AppUtil.playerOwns(token)) { return; } renderer.centerOn(token); renderer.maybeForcePlayersView(); if (TabletopTool.getPlayer().isGM()) { // If the user is a GM, recenter on the token. renderer.centerOn(token); // Update player view if necessary if (token.isToken() && token.isVisible()) { renderer.maybeForcePlayersView(); } } else if (renderer.getZone().isTokenVisible(token)) { // Not the GM, but the token is visible to the // ... what? renderer.centerOn(token); } } } }); } else if (SwingUtilities.isRightMouseButton(e)) { TokenInitiative ti = (TokenInitiative) displayList.getModel().getElementAt(displayList.locationToIndex(e.getPoint())); if (ti == null) { return; } displayList.setSelectedIndex(model.getDisplayIndex(list.indexOf(ti))); // TODO Can I use hasOwnerPermission(ti.getToken()) here instead? if (!hasGMPermission() && ti.getToken() != null && !ti.getToken().isOwner(TabletopTool.getPlayer().getName())) return; Set<GUID> tokens = Collections.singleton(ti.getId()); Set<TokenInitiative> tis = Collections.singleton(ti); new InitiativeTokenPopupMenu(tokens, tis, e.getX(), e.getY(), TabletopTool.getFrame().getCurrentZoneRenderer(), ti.getToken(), ti).showPopup(displayList); } // endif } } }