/**
* This program 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 2 of the License, or (at your option) any later
* version. This program 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, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.cspoker.client.gui.swt.window;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import org.apache.log4j.Logger;
import org.cspoker.client.common.playerstate.PlayerState;
import org.cspoker.client.gui.swt.control.CardPaintListener;
import org.cspoker.client.gui.swt.control.Chip;
import org.cspoker.client.gui.swt.control.ClientGUI;
import org.cspoker.client.gui.swt.control.UserSeatedPlayer;
import org.cspoker.common.api.shared.exception.IllegalActionException;
import org.cspoker.common.elements.cards.Card;
import org.cspoker.common.elements.player.PlayerId;
import org.cspoker.common.elements.player.SeatedPlayer;
import org.cspoker.common.elements.table.SeatId;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
/**
* Represents a composite in the TableComposite where the player is visualized
* with his user name, optional avatar, stack and status information
*/
public class PlayerSeatComposite
extends ClientComposite {
MouseAdapter sitInMouseAdapter = new SitInMouseAdapter();
private final class SitInMouseAdapter
extends MouseAdapter {
/**
* Upon clicking on the PlayerSeatComposite, the user sits in.
*
* @param evt MouseEvent (ignored)
* @see org.eclipse.swt.events.MouseAdapter#mouseDown(org.eclipse.swt.events.MouseEvent)
*/
@Override
public void mouseDown(MouseEvent evt) {
// Check if seat is occupied
if (playerId != null) {
return;
}
// If not, we can probably sit in
// TODO Buyin, reserve seat in the meantime
logger.debug("Clicked on PlayerSeatComposite, sit in if empty ...");
GameWindow containingGameWindow = getParent().getParent();
UserSeatedPlayer user = containingGameWindow.getUser();
PlayerState userSnapshot = getGameState().getPlayer(playerId);
if (userSnapshot != null) {
// Dont do anything if the user is already sitting in
return;
}
int amount = new BuyinDialog(getClientCore(), user.getCashierContext(), containingGameWindow
.getDetailedTable().getTableConfiguration().getBigBlind() * 100, true).open();
if (amount <= 0)
return;
try {
playerId = user.getId();
user.sitIn(seatId, amount);
// Update the button accordingly with which the user can sit
// in and out
Button sitInOutButton = getParent().getParent().getUserInputComposite().sitInOutButton;
sitInOutButton.setText("Sit Out");
sitInOutButton.setSelection(true);
sitInOutButton.setVisible(true);
} catch (RemoteException e) {
getClientCore().handleRemoteException(e);
} catch (IllegalActionException e) {
logger.error("This should not happen", e);
}
}
}
private final static Logger logger = Logger.getLogger(PlayerSeatComposite.class);
// Game-relevant fields
private PlayerId playerId;
private int numberOfHoleCards = 2;
private String name;
private final SeatId seatId;
/** Indicates whether this player is currently to act */
private boolean toAct;
// SWT fields
private Label playerName;
private final ProgressBar timeLeftBar = new ProgressBar(this, SWT.SMOOTH);
private final Runnable progressUpdater = new Runnable() {
private final Color colorDefault = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
private final Color colorAlternative = Display.getDefault().getSystemColor(SWT.COLOR_DARK_YELLOW);
final int maximum = timeLeftBar.getMaximum();
/**
* @see java.lang.Runnable#run()
*/
public void run() {
for (final int[] i = new int[1]; i[0] <= maximum; i[0]++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
playerName.setBackground(colorDefault);
playerName.update();
return;
}
if (getDisplay().isDisposed())
return;
getDisplay().syncExec(new Runnable() {
public void run() {
// TODO Blink here (border?!)...
if (timeLeftBar.isDisposed() || !timeLeftBar.isEnabled())
return;
timeLeftBar.setSelection(i[0]);
Color color = i[0] % 2 == 0 ? colorDefault : colorAlternative;
playerName.setBackground(color);
playerName.update();
}
});
}
}
};
private Composite holeCardsComposite;
private Label playerStack;
private Canvas chipsArea;
private Future<?> timerAction = new FutureTask<Object>(progressUpdater, null);
private List<Card> holeCards = new ArrayList<Card>();
/**
* @param parent
* @param style
* @param seatId
*/
public PlayerSeatComposite(TableComposite parent, int style, SeatId seatId) {
super(parent, style);
// numberOfHoleCards = gameState.getNumberOfHoleCards();
// TODO Make generic for other games
numberOfHoleCards = 2;
playerId = null;
this.seatId = seatId;
initGUI();
updatePlayerInfo();
}
/**
* Clears all hole cards
*/
public void clearHoleCards() {
holeCards.clear();
holeCardsComposite.redraw();
}
/**
* @return The {@link Rectangle} where the chips the player has bet in this
* round are displayed.
* <p>
* May be modified during animation threads. Reset to initial
* location via {@link #resetBetChipsDisplayArea()}
*/
public Rectangle getDealerChipLocation() {
int size = Math.min(Chip.MAX_IMG_SIZE, getParent().getSize().x / 200);
Image dealerChip = Chip.DEALER.getImage(size);
Rectangle pcLocation = getBounds();
int x = 0;
int y = 0;
int width = dealerChip.getImageData().width;
int height = dealerChip.getImageData().height;
switch (seatId.getId()) {
case 0:
case 5:
x = pcLocation.x + pcLocation.width;
y = pcLocation.y + pcLocation.height;
break;
case 1:
x = pcLocation.x - width;
y = pcLocation.y + pcLocation.height;
break;
case 2:
case 3:
x = pcLocation.x - width;
y = pcLocation.y;
break;
case 4:
x = pcLocation.x + pcLocation.width;
y = pcLocation.y;
break;
}
return new Rectangle(x, y, width, height);
}
private void initGUI() {
GridLayout layout = new GridLayout(1, false);
layout.horizontalSpacing = 1;
layout.verticalSpacing = 1;
layout.marginHeight = 1;
layout.marginWidth = 1;
setLayout(new GridLayout(1, false));
GridData data = new GridData(SWT.CENTER, SWT.CENTER, true, true);
data.widthHint = 150;
data.heightHint = 200;
data.minimumWidth = 80;
data.minimumHeight = 100;
setLayoutData(data);
{
playerName = new Label(this, SWT.SHADOW_NONE | SWT.CENTER | SWT.BORDER);
GridData player1NameLData = new GridData(SWT.DEFAULT, SWT.DEFAULT);
player1NameLData.horizontalAlignment = GridData.CENTER;
player1NameLData.widthHint = 100;
player1NameLData.minimumWidth = 55;
player1NameLData.grabExcessHorizontalSpace = true;
playerName.setLayoutData(player1NameLData);
playerName.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
}
{
playerStack = new Label(this, SWT.SHADOW_NONE | SWT.CENTER | SWT.BORDER);
GridData player1StackLData = new GridData(SWT.DEFAULT, SWT.DEFAULT);
player1StackLData.horizontalAlignment = GridData.CENTER;
player1StackLData.grabExcessHorizontalSpace = true;
player1StackLData.minimumWidth = 55;
playerStack.setLayoutData(player1StackLData);
playerStack.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
}
{
GridData timeLeftBarLData = new GridData(SWT.DEFAULT, SWT.DEFAULT);
timeLeftBarLData.horizontalAlignment = GridData.CENTER;
timeLeftBarLData.widthHint = 80;
timeLeftBarLData.heightHint = 10;
timeLeftBarLData.minimumWidth = 40;
timeLeftBarLData.grabExcessHorizontalSpace = true;
timeLeftBar.setLayoutData(timeLeftBarLData);
}
{
holeCardsComposite = new Composite(this, SWT.NONE | ClientGUI.COMPOSITE_BORDER_STYLE);
holeCardsComposite.setLayout(new GridLayout(numberOfHoleCards, true));
GridData holeCardsCompositeLayoutData = new GridData(SWT.CENTER, SWT.CENTER, true, true);
holeCardsCompositeLayoutData.minimumWidth = numberOfHoleCards * ClientGUI.MINIMUM_CARD_WIDTH;
holeCardsCompositeLayoutData.minimumHeight = ClientGUI.MINIMUM_CARD_HEIGHT;
holeCardsCompositeLayoutData.widthHint = numberOfHoleCards * ClientGUI.PREFERRED_CARD_WIDTH - 10;
holeCardsCompositeLayoutData.heightHint = ClientGUI.PREFERRED_CARD_HEIGHT;
holeCardsComposite.setLayoutData(holeCardsCompositeLayoutData);
holeCardsComposite.addPaintListener(new CardPaintListener(holeCards, 2, SWT.CENTER, -10));
}
this.addMouseListener(sitInMouseAdapter);
for (Control c : getChildren()) {
c.addMouseListener(sitInMouseAdapter);
}
}
// @Override
// public void onPlayerReboughtEvent(PlayerReboughtEvent event) {
// player = event.getPlayer();
// setStack(player.getStackValue());
//
// }
// @Override
// public void onPlayerSatOutEvent(PlayerSatOutEvent event)
// throws RemoteException {
// playerName.setText(event.getPlayer().getName() + " \n (Sitting Out)");
// }
/**
* @param cards The cards to display. If <code>null</code> is passed,
* display the back of the cards
*/
public void setHoleCards(Collection<Card> cards) {
holeCards.clear();
holeCards.addAll(cards);
holeCardsComposite.redraw();
}
/**
* Refreshes chips and briefly displays the action the player has taken in
* place of his user name.
*
* @param action The action to display, i.e. <i>"Check"</i> or <i>"Fold"</i>
*/
public void showAction(final String action) {
getChipsArea().setVisible(false);
Rectangle chipsArea = getChipsArea().getBounds();
getParent().redraw(chipsArea.x, chipsArea.y, chipsArea.width, chipsArea.height, true);
getParent().update();
PlayerState player = getGameState().getPlayer(playerId);
playerStack.setText(ClientGUI.formatBet(player.getStack()));
playerStack.pack(true);
final String displayedName = name;
playerName.setText(action);
playerName.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLUE));
getDisplay().timerExec(3000, new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
// Check because this may have been outdated in the meantime
// (sit out etc.)
if (!playerName.isDisposed()) {
playerName.setText(displayedName);
playerName.setForeground(Display.getDefault().getSystemColor(SWT.DEFAULT));
}
}
});
layout(true);
}
/**
* Resets and starts the progress bar above the player's name, indicating
* that it is this player's turn to act.
*/
private void startTimer() {
timeLeftBar.setEnabled(true);
timeLeftBar.setVisible(true);
timeLeftBar.setSelection(0);
timeLeftBar.setMaximum(30);
// Update Progress Bar
timerAction.cancel(true);
timerAction = executor.submit(progressUpdater);
}
/**
* Stops the Progress Bar timer (if it is running)
*/
private void stopTimer() {
timerAction.cancel(true);
playerName.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
playerName.update();
timeLeftBar.setEnabled(false);
timeLeftBar.setVisible(false);
}
public void setActive(boolean active) {
this.toAct = active;
if (active) {
startTimer();
} else {
stopTimer();
}
}
/**
* Update with detailed information after a player sits in.
*
* @param detailedPlayer The player to use as an update.
*/
public void occupy(SeatedPlayer detailedPlayer) {
// Check is necessary because the variable might already be set to a
// UserSeatedPlayer
if (playerId == null) {
playerId = detailedPlayer.getId();
} else {
logger.warn("Trying to occupy seat with " + detailedPlayer + ", " + playerId + " already sitting here");
}
name = detailedPlayer.getName();
playerName.setForeground(Display.getDefault().getSystemColor(SWT.DEFAULT));
playerName.setVisible(true);
playerName.setText(name);
playerStack.setVisible(true);
playerStack.setText(ClientGUI.formatBet(detailedPlayer.getStackValue()));
update();
}
/**
* Reset this {@link PlayerSeatComposite}, indicating an empty seat which
* may be occupied by clicking on it
*/
public void updatePlayerInfo() {
PlayerState player = null;
if (playerId != null) {
player = getGameState().getPlayer(playerId);
}
String displayedName = (name == null) ? "Empty Seat" : name;
if (player != null && false ) {//TODO !player.sitsIn()
displayedName = displayedName.concat(" (Sitting Out)");
}
playerName.setText(displayedName);
if (player == null) {
name = null;
return;
}
playerStack.setText(ClientGUI.formatBet(player.getStack()));
playerStack.pack(true);
holeCardsComposite.redraw();
timerAction.cancel(true);
layout(true);
}
/**
* @return The immutable seat id associated with this PlayerSeatComposite
*/
public SeatId getSeatId() {
return seatId;
}
@Override
public TableComposite getParent() {
return (TableComposite) super.getParent();
}
public Canvas getChipsArea() {
return chipsArea;
}
public void setChipsArea(Canvas chipsArea) {
this.chipsArea = chipsArea;
}
public boolean isToAct() {
return toAct;
}
/**
* @return
*/
public PlayerId getPlayerId() {
return playerId;
}
}