/*
* 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.model.initiative;
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.AbstractListModel;
import com.t3.client.AppUtil;
import com.t3.client.TabletopTool;
import com.t3.model.Token;
import com.t3.model.Token.Type;
import com.t3.model.Zone;
import com.t3.model.initiative.InitiativeList.TokenInitiative;
/**
* This implements a list model for the for the panel. It removes all of the tokens that aren't
* visible to players if needed.
*
* @author Jay
*/
public class InitiativeListModel extends AbstractListModel<TokenInitiative> implements PropertyChangeListener {
/*---------------------------------------------------------------------------------------------
* Instance Variables
*-------------------------------------------------------------------------------------------*/
/**
* All of the tokens for this initiative list.
*/
private InitiativeList list;
/*---------------------------------------------------------------------------------------------
* Instance Methods
*-------------------------------------------------------------------------------------------*/
/**
* Get the token with the current initiative. Handle GM vs. Player
*
* @return The current token displayed to the user or <code>null</code> if there is no current token.
* May be different for GM and Player
*/
public TokenInitiative getCurrentTokenInitiative() {
if (list.getCurrent() < 0) return null;
if (TabletopTool.getFrame().getInitiativePanel().hasGMPermission())
return list.getTokenInitiative(list.getCurrent());
TokenInitiative visible = null;
for (int i = 0; i <= list.getCurrent(); i++) {
TokenInitiative ti = list.getTokenInitiative(i);
Token token = ti.getToken();
if (token != null && isTokenVisible(ti.getToken(), list.isHideNPC())) visible = ti;
} // endfor
return visible;
}
/**
* Get the display index for the token at the passed list index
*
* @param index The list index of a token;
* @return The index in the display model or -1 if the item is not displayed.
*/
public int getDisplayIndex(int index) {
if (index < 0 || TabletopTool.getFrame().getInitiativePanel().hasGMPermission())
return index;
if (!isTokenVisible(list.getToken(index), list.isHideNPC())) return -1;
int found = -1;
for (int i = 0; i <= index; i++)
if (isTokenVisible(list.getToken(i), list.isHideNPC())) found += 1;
return found;
}
/**
* Called when the underlying tokens have been changed.
*/
public void updateModel() {
fireContentsChanged(this, 0, getSize() - 1);
}
/** @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
int oldCount = 0;
if (list != null) {
list.removePropertyChangeListener(this);
oldCount = getSize();
} // endif
// Add the new one
list = theList;
int newCount = 0;
if (list != null) {
list.addPropertyChangeListener(this);
newCount = getSize();
} // endif
// Fire events
if (oldCount > 0 || newCount > 0) {
if (oldCount > newCount) {
fireIntervalRemoved(this, newCount, oldCount - 1);
} else if (oldCount < newCount) {
fireIntervalAdded(this, oldCount, newCount - 1);
} // endif
fireContentsChanged(this, 0, Math.min(newCount, oldCount) - 1);
} // endif
}
/**
* Is the passed token displayed in the list?
*
* @param token Token being displayed
* @param hideNPC Flag indicating that NPC's are hidden.
* @return The value <code>true</code> if this token is shown to the user.
*/
public static boolean isTokenVisible(Token token, boolean hideNPC) {
if (token == null) return false;
if (TabletopTool.getFrame().getInitiativePanel().hasGMPermission()) return true;
if (!token.isVisible() || token.getLayer() == Zone.Layer.GM) return false;
if (token.isVisibleOnlyToOwner() && !AppUtil.playerOwns(token)) return false;
if (hideNPC && token.getType() == Type.NPC) return false;
return true;
}
/*---------------------------------------------------------------------------------------------
* PropertyChangeEvent Interface Methods
*-------------------------------------------------------------------------------------------*/
/**
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
// Handle by property name
if (evt.getPropertyName().equals(InitiativeList.CURRENT_PROP)) {
// Change the two old and new token with initiative
int oldIndex = getDisplayIndex(((Integer)evt.getOldValue()).intValue());
int newIndex = getDisplayIndex(((Integer)evt.getNewValue()).intValue());
if (oldIndex != newIndex) {
if (oldIndex != -1)
fireContentsChanged(InitiativeListModel.this, oldIndex, oldIndex);
if (newIndex != -1)
fireContentsChanged(InitiativeListModel.this, newIndex, newIndex);
} // endif
} else if (evt.getPropertyName().equals(InitiativeList.TOKENS_PROP)) {
if (evt instanceof IndexedPropertyChangeEvent) {
int index = ((IndexedPropertyChangeEvent)evt).getIndex();
if (evt.getOldValue() == null && evt.getNewValue() instanceof TokenInitiative) {
// Inserted a token
if (isTokenVisible(list.getToken(index), list.isHideNPC())) {
int displayIndex = getDisplayIndex(index);
fireIntervalAdded(InitiativeListModel.this, displayIndex, displayIndex);
} // endif
} else if (evt.getNewValue() == null & evt.getOldValue() instanceof TokenInitiative) {
// Removed a token
TokenInitiative ti=(TokenInitiative)evt.getOldValue();
if (ti.tokenExists() && isTokenVisible(ti.getToken(), list.isHideNPC())) {
fireIntervalRemoved(InitiativeListModel.this, list.getSize(), list.getSize());
fireContentsChanged(InitiativeListModel.this, 0, list.getSize() - 1);
}
} else {
// Update a token
if (isTokenVisible(list.getToken(index), list.isHideNPC())) {
int displayIndex = getDisplayIndex(index);
fireContentsChanged(InitiativeListModel.this, displayIndex, displayIndex);
}
} // endif
} else if (evt.getPropertyName().equals(InitiativeList.HIDE_NPCS_PROP)) {
// Changed visibility of NPC tokens.
List<TokenInitiative> tokens = list.getTokens();
int oldSize = getSize(tokens, ((Boolean)evt.getOldValue()).booleanValue());
int newSize = getSize(tokens, ((Boolean)evt.getNewValue()).booleanValue());
if (oldSize > newSize) {
fireIntervalRemoved(InitiativeListModel.this, newSize, oldSize - 1);
} else if (newSize > oldSize) {
fireIntervalAdded(InitiativeListModel.this, oldSize, newSize - 1);
} // endif
fireContentsChanged(InitiativeListModel.this, 0, Math.min(oldSize, newSize));
} else {
if (evt.getOldValue() instanceof List && evt.getNewValue() instanceof List && ((List)evt.getNewValue()).isEmpty()) {
// Did a clear, delete everything
List<TokenInitiative> tokens = (List<TokenInitiative>)evt.getOldValue();
fireIntervalRemoved(InitiativeListModel.this, 0, getSize(tokens, list.isHideNPC()));
} else if (evt.getOldValue() == null && evt.getNewValue() instanceof List) {
// Just sorted, update everything
List<TokenInitiative> tokens = (List<TokenInitiative>)evt.getNewValue();
fireContentsChanged(InitiativeListModel.this, 0, getSize(tokens, list.isHideNPC()));
} // endif
} // endif
}
}
/**
* Get the number of visible tokens in a list;
*
* @param tokens Search for visible tokens in this list.
* @param hideNPC Should the NPC's be hidden?
* @return The number of visible tokens.
*/
private int getSize(List<TokenInitiative> tokens, boolean hideNPC) {
if (tokens == null || tokens.isEmpty()) return 0;
int size = 0;
if (TabletopTool.getFrame().getInitiativePanel().hasGMPermission()) {
size = tokens.size();
} else {
for (TokenInitiative ti : tokens)
if (isTokenVisible(ti.getToken(), hideNPC)) size += 1;
}
return size;
}
/*---------------------------------------------------------------------------------------------
* ListModel Interface Methods
*-------------------------------------------------------------------------------------------*/
/**
* Get the token initiative at the passed display index. Handle GM vs. Player
*
* @see javax.swing.ListModel#getElementAt(int)
*/
@Override
public TokenInitiative getElementAt(int index) {
if (TabletopTool.getFrame().getInitiativePanel().hasGMPermission())
return list.getTokenInitiative(index);
int found = index;
for (int i = 0; i < list.getSize(); i++) {
TokenInitiative ti = list.getTokenInitiative(i);
if (isTokenVisible(ti.getToken(), list.isHideNPC())) {
found -= 1;
if (found == -1) return ti;
}
}
return null;
}
/**
* Get the size of the list model, handle GM vs. Player.
*
* @see javax.swing.ListModel#getSize()
*/
@Override
public int getSize() {
if (list == null) {
return 0;
}
if (TabletopTool.getFrame() == null || TabletopTool.getFrame().getInitiativePanel().hasGMPermission())
return list.getSize();
int size = 0;
for (int i = 0; i < list.getSize(); i++) {
TokenInitiative ti = list.getTokenInitiative(i);
if (isTokenVisible(ti.getToken(), list.isHideNPC())) size += 1;
} // endfor
return size;
}
}