package net.sf.colossus.webclient;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import net.sf.colossus.client.Client;
import net.sf.colossus.client.Client.ConnectionInitException;
import net.sf.colossus.common.Constants;
import net.sf.colossus.common.Options;
import net.sf.colossus.common.WhatNextManager;
import net.sf.colossus.common.WhatNextManager.WhatToDoNext;
import net.sf.colossus.gui.ColumnWidthPersistingJTable;
import net.sf.colossus.guiutil.KFrame;
import net.sf.colossus.server.INotifyWebServer;
import net.sf.colossus.server.Server;
import net.sf.colossus.util.HTMLColor;
import net.sf.colossus.util.Split;
import net.sf.colossus.util.ViableEntityManager;
import net.sf.colossus.webclient.WebClientSocketThread.WcstException;
import net.sf.colossus.webcommon.GameInfo;
import net.sf.colossus.webcommon.GameInfo.GameState;
import net.sf.colossus.webcommon.IGameRunner;
import net.sf.colossus.webcommon.IWebClient;
import net.sf.colossus.webcommon.IWebServer;
import net.sf.colossus.webcommon.User;
/**
* This is the main class for one user client for the web server.
* One such client can register and/or login to the web server,
* propose a game, browse proposed games and enroll to such a game.
* When a game has enough players, it can be started, and this
* brings up the MasterBoard like the network client would do.
*
* @author Clemens Katzer
*/
public class WebClient extends KFrame implements IWebClient
{
private static final Logger LOGGER = Logger.getLogger(WebClient.class
.getName());
// 1: webclient understands deliverGeneralMessage
// 2: webclient supports ping
// 3: webclient knows DinoTitan variant;
// 4: now all options can be selected in webclient, also
// teleport options added
// 5: can properly display suspended games
// 6: version from 6.1.2016. Scratch-reconnect stuff ongoing
// 7: admin can delete suspened games via gui
// 8: joinToWatch should work again. Also other changes that
// might help with the 'no BattleBoard' problem
public final static int WC_VERSION_GENERAL_MESSAGE = 1;
public final static int WC_VERSION_SUPPORTS_PING = 2;
public final static int WC_VERSION_DINO_OK = 3;
public final static int WC_VERSION_SUPPORTS_EXTRA_OPTIONS = 4;
public final static int WC_VERSION_RESUME = 5;
public final static int WC_VERSION_SCRATCH_RECONN_WIP = 6;
public final static int WC_VERSION_DELETE_SUSPENDED_GAME = 7;
public final static int WC_VERSION_WATCH_GAME_FIXED = 8;
final static int WEB_CLIENT_VERSION = WC_VERSION_WATCH_GAME_FIXED;
// TODO make this all based on Locale.getDefault()
// Initially: use German. To make it variable, need also to set
// the default values / info texts according to Locale.
final static Locale myLocale = Locale.GERMANY;
final static String CARD_PROPOSED = "proposed";
final static String TYPE_SCHEDULED = "scheduled";
final static String TYPE_INSTANTLY = "instantly";
// they are used both as button text and key in Options
public final static String AutoLoginCBText = "Auto-login on start";
public final static String AutoGamePaneCBText = "After login Game pane";
public static final String PLAYERS_MISSING = "MissingPlayers";
private final WhatNextManager whatNextManager;
private String hostname;
private int port;
private String login;
private String username;
private String password;
private String email;
private boolean isAdmin = false;
private final Options options;
private Client gameClient;
private RunGameInSameJVM gameRunner;
private Server localServer = null;
private String startedGameId = null;
private int startedAtPort;
private String startedAtHost;
private RegisterPasswordPanel registerPanel;
private final Object comingUpMutex = new Object();
private boolean timeIsUp;
private boolean clientIsUp;
private boolean clientStartFailed;
private final static int NotLoggedIn = 1;
private final static int LoggedIn = 2;
// Enrolled to an instant game
private final static int EnrolledInstantGame = 3;
private final static int Playing = 4;
private final static int Watching = 5;
// private final static int PlayingButDead = 6;
private boolean gameResumeInitiated = false;
private GameInfo startingGame = null;
// boundaries which port nr user may enter in the ServerPort field:
private final static int minPort = 1;
private final static int maxPort = 65535;
// needed basically only to validate field contents (they must not contain
// the separator)
private final static String sep = IWebServer.WebProtocolSeparator;
private boolean failedDueToDuplicateLogin = false;
private boolean failedDueToOwnCancel = false;
private int state = NotLoggedIn;
private String enrolledInstantGameId = null;
private String watchingInstantGameId = null;
private boolean scheduledGamesMode;
private int usersLoggedIn = 0;
private int usersEnrolled = 0;
private int usersPlaying = 0;
private int usersDead = 0;
private long usersLogoffAgo = 0;
private String usersText = "";
private IWebServer server = null;
private WebClientSocketThread wcst = null;
private JTabbedPane tabbedPane;
private Box serverTab;
private JPanel settingsPanel;
private JPanel gameOptionsPanel;
private Box createGamesTab;
private Box runningGamesTab;
private Box suspendedGamesTab;
private Box adminTab;
private final Point defaultLocation = new Point(600, 100);
private JLabel statusLabel;
private JLabel userinfoLabel; // Server/Login pane:
private JTextField webserverHostField;
private JTextField webserverPortField;
private JTextField loginField;
private JPasswordField passwordField;
private JTextField commandField;
private JLabel receivedField;
private JButton loginLogoutButton;
private JButton quitButton;
private JButton contactAdminButton;
private JLabel registerOrPasswordLabel;
private JButton registerOrPasswordButton;
private JButton debugSubmitButton;
private JButton rereadLoginMsgButton;
private JButton shutdownButton;
private JButton dumpInfoButton;
private JTextField notifyMessageField;
private JTextField notifyUserField;
private JTextField beepCountField;
private JTextField beepIntervalField;
private JLabel statusField;
private String statusText = "";
// Game browsing pane:
private JComboBox<String> variantBox;
private JComboBox<String> viewmodeBox;
private JComboBox<String> eventExpiringBox;
private JSpinner spinner1;
private JSpinner spinner2;
private JSpinner spinner3;
private JLabel maxLabel;
private JLabel nowDateAndTimeLabel;
private JTextField atDateField;
private JTextField atTimeField;
private JTextField durationField;
private JTextField summaryText;
private DateFormat myDateFormat;
private DateFormat myTimeFormat;
private JButton proposeButton;
private JButton cancelButton;
private JButton enrollButton;
private JButton unenrollButton;
private JButton startButton;
private JButton startLocallyButton;
private JButton watchButton;
private JButton resumeButton;
private JButton deleteButton;
private JLabel reasonWhyNotLabel;
private JButton recoverButton;
private JButton hideButton;
private JLabel hideButtonText;
private JRadioButton autoGSNothingRB;
private JRadioButton autoGSHideRB;
private JRadioButton autoGSCloseRB;
private JLabel infoTextLabel;
final static String needLoginText = "You need to login to browse or propose Games.";
final static String enrollText = "Propose or Enroll, and when enough players have enrolled, the game creator can press 'Start'.";
final static String startClickedText = "Request to start game was sent to server, please wait...";
final static String waitingText = "Client connected successfully, waiting for all other players. Please wait...";
final static String enrolledText = "NOTE: While enrolled to an instant game, you can't propose or enroll to other instant games.";
final static String playingText = "While playing, you can't propose or enroll to other instant games.";
final static String watchingText = "While playing or watching, you can't propose or enroll to other instant games.";
private ChatHandler generalChat;
private final ArrayList<GameInfo> gamesUpdates = new ArrayList<GameInfo>();
/**
* NOTE: shared with SocketThread, because WCST needs it to restore
* game tokens to an GameInfo object
*/
private final HashMap<String, GameInfo> gameHash = new HashMap<String, GameInfo>();
private final HashSet<String> deletedGames = new HashSet<String>();
// so that we can for all checked checkboxes add the corresponding option
// to the options string
private final HashMap<String, JCheckBox> checkboxForOption = new HashMap<String, JCheckBox>();
// Need a list to preserve order
private final List<String> gameOptions = new LinkedList<String>(
Arrays.asList(Options.unlimitedMulligans, Options.balancedTowers,
Options.sansLordAutoBattle, Options.pbBattleHits,
Options.inactivityTimeout));
// Need a list to preserve order
private final List<String> teleportOptions = new LinkedList<String>(
Arrays.asList(Options.noFirstTurnT2TTeleport,
Options.noFirstTurnTeleport, Options.towerToTowerTeleportOnly,
Options.noTowerTeleport, Options.noTitanTeleport,
Options.noFirstTurnWarlockRecruit));
private JPanel gamesTablesPanel;
private JPanel gamesCards;
private JPanel propGamesCard;
// proposed games
private JTable proposedGameTable;
private GameTableModel proposedGameDataModel;
// running games
private JTable runGameTable;
private GameTableModel runGameDataModel;
// private ListSelectionModel runGameListSelectionModel;
private JTable suspGameTable;
private GameTableModel suspGameDataModel;
private static String windowTitle = "Web Client";
private final static String LoginButtonText = "Login";
private final static String LogoutButtonText = "Logout";
private final static String CancelLoginButtonText = "Cancel";
private final static String quitButtonText = "Quit";
private final static String contactAdminButtonText = "Contact the administrator";
private final static String contactAdminButtonDisabledText = " <dialog still open> ";
private final static String HideButtonText = "Hide Web Client";
private final static String WatchButtonText = "Join game as spectator";
private final static String CantHideText = "(You can hide web client only if game client is open)";
private final static String HowtoUnhideText = "You can get web client back from MasterBoard - Window menu";
private final static String createAccountButtonText = "Register";
private final static String chgPasswordButtonText = "Change password";
private final static String ProposeButtonText = "Propose";
private final static String EnrollButtonText = "Enroll";
private final static String UnenrollButtonText = "Unenroll";
private final static String CancelButtonText = "Cancel";
private final static String StartButtonText = "Start";
private final static String StartLocallyButtonText = "Start locally";
private final static String createAccountLabelText = "No login yet? Create one:";
private final static String chgPasswordLabelText = "Change your password:";
private final static String AutoGameStartActionNothing = "Do nothing";
private final static String AutoGameStartActionHide = "Hide WebClient";
private final static String AutoGameStartActionClose = "Close WebClient";
private final static String optAutoGameStartAction = "Auto Game Start Action";
private final static String defaultSummaryText = "Let's play!";
public WebClient(WhatNextManager whatNextManager, String hostname,
int port, String login, String password)
{
super(windowTitle);
this.whatNextManager = whatNextManager;
options = new Options(Constants.OPTIONS_WEB_CLIENT_NAME);
options.loadOptions();
// Initialize those 4 values + username from either given
// arguments, loaded from cf file, or reasonable default.
initValues(hostname, port, login, password);
ViableEntityManager.register(this, "WebClient " + login);
net.sf.colossus.util.InstanceTracker.register(this, "WebClient "
+ login);
if (SwingUtilities.isEventDispatchThread())
{
setupGUI();
autoActions();
}
else
{
try
{
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
setupGUI();
autoActions();
}
});
}
catch (InterruptedException e)
{/* ignore */
}
catch (InvocationTargetException e2)
{/* ignore */
}
}
}
public int getClientVersion()
{
return WEB_CLIENT_VERSION;
}
private void initValues(String hostname, int port, String login,
String password)
{
if (hostname != null && !hostname.equals(""))
{
this.hostname = hostname;
}
else
{
String cfHostname = options.getStringOption(Options.webServerHost);
if (cfHostname != null && !cfHostname.equals(""))
{
this.hostname = cfHostname;
}
else
{
this.hostname = Constants.defaultWebServer;
}
}
if (port > 0)
{
this.port = port;
}
else
{
int cfPort = options.getIntOption(Options.webServerPort);
if (cfPort >= 1)
{
this.port = cfPort;
}
else
{
this.port = Constants.defaultWebPort;
}
}
// Initialize this already here, password login depends on it.
String cfLogin = options.getStringOption(Options.webClientLogin);
// Use -m argument, if given; otherwise from cf or default.
if (login != null && !login.equals(""))
{
this.login = login;
}
else
{
if (cfLogin != null && !cfLogin.equals(""))
{
this.login = cfLogin;
}
else
{
this.login = Constants.username;
}
}
// Right now, login and username are the same, but we may change
// that one point.
this.username = this.login;
// right now password is never given but...
if (password != null)
{
this.password = password;
}
else
{
String cfPassword = options
.getStringOption(Options.webClientPassword);
if (cfPassword != null && !cfPassword.equals("")
&&
// Use stored password only if its same user:
cfLogin != null && cfLogin.equals(this.login)
&& !this.login.equals(""))
{
this.password = cfPassword;
}
else
{
// For debug/development purposes (when usernames do not
// match), instead of empty password use the one from property
// - this is helpful because I usually need 2 or more clients
// and without this here I would have to type in a real
// password for at least one of them, every time...
String DEBUG_PASSWD_PROP = "wcpasswd";
String debugPassword = System.getProperty(DEBUG_PASSWD_PROP);
if (debugPassword != null)
{
this.password = debugPassword;
}
else
{
this.password = "";
}
}
}
}
public void setGameClient(Client c)
{
this.gameClient = c;
if (c == null)
{
hideButton.setEnabled(false);
hideButtonText.setText(CantHideText);
if (state == Playing || state == Watching)
{
enrolledInstantGameId = null;
clearWatching();
updateGUI();
}
}
else
{
hideButton.setEnabled(true);
hideButtonText.setText(HowtoUnhideText);
}
}
private String getOngoingGameId()
{
if (gameClient != null)
{
return gameClient.getGUI().getGameId();
}
return "<unknown>";
}
private void setScheduledGamesMode(boolean scheduled)
{
scheduledGamesMode = scheduled;
atDateField.setEnabled(scheduled);
atTimeField.setEnabled(scheduled);
if (propGamesCard == null || gamesCards == null)
{
return;
}
}
public boolean getScheduledGamesMode()
{
return scheduledGamesMode;
}
public void onGameStartAutoAction()
{
if (SwingUtilities.isEventDispatchThread())
{
doAutoGSAction();
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
doAutoGSAction();
}
});
}
}
private void setupGUI()
{
getContentPane().setLayout(new BorderLayout());
// Top of the frame: login and users status/infos:
Box headPane = new Box(BoxLayout.Y_AXIS);
statusLabel = new JLabel("login status");
userinfoLabel = new JLabel("user info status");
headPane.add(statusLabel);
headPane.add(userinfoLabel);
getContentPane().add(headPane, BorderLayout.NORTH);
// A tabbed pane for the various tabs in the CENTER:
tabbedPane = new JTabbedPane();
tabbedPane.setPreferredSize(new Dimension(900, 600)); // width x height
tabbedPane.setMinimumSize(new Dimension(900, 530)); // width x height
// Box mainPane = new Box(BoxLayout.Y_AXIS);
// mainPane.add(tabbedPane);
getContentPane().add(tabbedPane, BorderLayout.CENTER);
// Now the different tabs of the tabbed pane:
createServerTab();
tabbedPane.addTab("Server", serverTab);
createCreateGamesTab();
tabbedPane.addTab("Create or Join", createGamesTab);
createRunningGamesTab();
tabbedPane.addTab("Running Games", runningGamesTab);
createSuspendedGamesTab();
tabbedPane.addTab("Suspended Games", suspendedGamesTab);
generalChat = new ChatHandler(IWebServer.generalChatName, "Chat",
server, username);
tabbedPane.addTab(generalChat.getTitle(), generalChat.getTab());
createAdminTab();
// adminTab is added to tabbedPane then/only when user has
// logged in and server informed us that this user is admin user
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
WhatNextManager whatNextManager = WebClient.this.whatNextManager;
whatNextManager.setWhatToDoNext(
WhatToDoNext.GET_PLAYERS_DIALOG, false);
dispose();
}
});
// TODO read from preferences?
// setScheduledGamesMode(getScheduledGamesMode());
// now finish all
pack();
useSaveWindow(options, "WebClient", defaultLocation);
setVisible(true);
}
private void autoActions()
{
if (options.getOption(WebClient.AutoLoginCBText))
{
String login = loginField.getText();
String password = new String(passwordField.getPassword());
// Eclipse warning says password can never be null. Well...
if (login != null && !login.equals("") && !password.equals(""))
{
doLogin();
}
}
else
{
doUpdateGUI();
}
}
private void doAutoGSAction()
{
String whatToDo = options.getStringOption(optAutoGameStartAction);
if (whatToDo == null)
{
return;
}
if (whatToDo.equals(AutoGameStartActionNothing))
{
// ok, nothing to do.
}
else if (whatToDo.equals(AutoGameStartActionHide))
{
this.setVisible(false);
}
else if (whatToDo.equals(AutoGameStartActionClose))
{
WebClient.this.whatNextManager.setWhatToDoNext(
WhatToDoNext.GET_PLAYERS_DIALOG, false);
dispose();
}
else
{
LOGGER.log(Level.WARNING,
"ooops! auto Game Start Action option is '" + whatToDo
+ "' ???");
}
}
public void updateStatus(String text, Color color)
{
this.statusText = text;
statusField.setText(text);
statusField.setForeground(color);
}
public void restoreStatusField()
{
if (state == NotLoggedIn)
{
updateStatus("Not connected", Color.red);
}
else
{
updateStatus("Logged in", Color.green);
}
}
private void addAdminTab()
{
tabbedPane.addTab("Admin", adminTab);
}
private void removeAdminTab()
{
tabbedPane.remove(adminTab);
}
private void setAdmin(boolean isAdmin)
{
this.isAdmin = isAdmin;
if (this.isAdmin)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
addAdminTab();
}
});
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
removeAdminTab();
}
});
}
}
public boolean isAdmin()
{
return isAdmin;
}
public void showAnswer(String s)
{
receivedField.setText(s);
}
public String getHost()
{
return webserverHostField.getText();
}
public String getPort()
{
return webserverPortField.getText();
}
public IGameRunner getGameRunner()
{
return gameRunner;
}
public INotifyWebServer getWhomToNotify()
{
return gameRunner;
}
private void createServerTab()
{
serverTab = new Box(BoxLayout.Y_AXIS);
Box connectionPane = new Box(BoxLayout.X_AXIS);
JPanel loginPane = new JPanel(new GridLayout(0, 2));
loginPane.setBorder(new TitledBorder("Connection information"));
loginPane.setPreferredSize(new Dimension(150, 200));
loginPane.add(new JLabel("Web Server"));
webserverHostField = new JTextField(this.hostname);
webserverHostField.addFocusListener(new FocusAdapter()
{
@Override
public void focusGained(FocusEvent e)
{
webserverHostField.selectAll();
}
});
// webserverHostField.addActionListener(this);
loginPane.add(webserverHostField);
loginPane.add(new JLabel("Port"));
webserverPortField = new JTextField(this.port + "");
webserverPortField.addFocusListener(new FocusAdapter()
{
@Override
public void focusGained(FocusEvent e)
{
webserverPortField.selectAll();
}
});
// webserverPortField.addActionListener(this);
loginPane.add(webserverPortField);
loginPane.add(new JLabel("Login id"));
loginField = new JTextField(this.login);
loginField.addFocusListener(new FocusAdapter()
{
@Override
public void focusGained(FocusEvent e)
{
loginField.selectAll();
}
});
// nameField.addActionListener(this);
loginPane.add(loginField);
AbstractAction showPwAction = new AbstractAction("*")
{
public void actionPerformed(ActionEvent e)
{
AbstractButton abstractButton = (AbstractButton)e.getSource();
boolean selected = abstractButton.getModel().isSelected();
passwordField.setEchoChar(selected ? '*' : (char)0);
}
};
Box pwFieldPanel = new Box(BoxLayout.X_AXIS);
passwordField = new JPasswordField(this.password);
pwFieldPanel.add(passwordField);
JCheckBox showPwCheckbox = new JCheckBox(showPwAction);
showPwCheckbox.setSelected(true);
pwFieldPanel.add(showPwCheckbox);
loginPane.add(new JLabel("Password"));
// passwordField.addActionListener(this);
loginPane.add(pwFieldPanel);
passwordField.addFocusListener(new FocusAdapter()
{
@Override
public void focusGained(FocusEvent e)
{
passwordField.selectAll();
}
});
loginLogoutButton = new JButton(LoginButtonText);
loginLogoutButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
loginLogoutButtonAction(e.getActionCommand());
}
});
loginLogoutButton.setEnabled(true);
loginPane.add(loginLogoutButton);
quitButton = new JButton(quitButtonText);
quitButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
quitButtonAction();
}
});
quitButton.setEnabled(true);
loginPane.add(quitButton);
loginPane.add(new JLabel("Connection status:"));
statusField = new JLabel(statusText);
loginPane.add(statusField);
updateStatus("Not connected", Color.red);
ActionListener cbActionListener = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
handleAction(e);
}
};
boolean alos = this.options.getOption(AutoLoginCBText);
JCheckBox autoLoginCB = new JCheckBox(AutoLoginCBText, alos);
autoLoginCB.addActionListener(cbActionListener);
loginPane.add(autoLoginCB);
loginPane.add(new JLabel(""));
boolean algp = this.options.getOption(AutoGamePaneCBText);
JCheckBox autoGamePaneCB = new JCheckBox(AutoGamePaneCBText, algp);
autoGamePaneCB.addActionListener(cbActionListener);
loginPane.add(autoGamePaneCB);
loginPane.add(new JLabel(""));
serverTab.add(connectionPane);
connectionPane.add(loginPane);
connectionPane.add(Box.createHorizontalGlue());
connectionPane.add(Box.createVerticalGlue());
serverTab.add(Box.createVerticalGlue());
serverTab.add(Box.createHorizontalGlue());
// Label can show: registerLabelText or chgPasswordLabelText
registerOrPasswordLabel = new JLabel(createAccountLabelText);
// Button can show: createAccountButtonText or chgPasswordButtonText
registerOrPasswordButton = new JButton(createAccountButtonText);
registerOrPasswordButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
registerOrPasswordButtonAction(e.getActionCommand());
}
});
loginPane.add(registerOrPasswordLabel);
loginPane.add(registerOrPasswordButton);
boolean SHOW_CONTACT_ADMIN_BUTTON = true;
if (SHOW_CONTACT_ADMIN_BUTTON)
{
createContactAdminButton(loginPane);
}
}
private void handleAction(ActionEvent e)
{
String text = e.getActionCommand();
Object component = e.getSource();
if (component instanceof JCheckBox)
{
JCheckBox cb = (JCheckBox)component;
options.setOption(text, cb.isSelected());
}
else
{
LOGGER.severe("Event source with text " + text
+ " is not a checkbox??");
}
}
private void createContactAdminButton(JPanel loginPane)
{
contactAdminButton = new JButton(contactAdminButtonText);
contactAdminButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
contactAdmin();
}
});
contactAdminButton.setEnabled(true);
loginPane.add(new JLabel("Problems, Questions, Feedback?"));
loginPane.add(contactAdminButton);
}
private void addRadioButton(Container cont, ButtonGroup group,
String text, String current, ItemListener listener)
{
// use same word for cmd as the text on it:
String cmd = text;
JRadioButton rb = new JRadioButton(text);
if (cmd != null && !cmd.equals(""))
{
rb.setActionCommand(cmd);
}
rb.addItemListener(listener);
group.add(rb);
cont.add(rb);
boolean selected = (text.equals(current));
rb.setSelected(selected);
}
protected static JLabel nonBoldLabel(String text)
{
JLabel l = new JLabel(text);
l.setFont(l.getFont().deriveFont(Font.PLAIN));
// l.setAlignmentX(Box.RIGHT_ALIGNMENT);
return l;
}
private Box makeTextBox(Component c)
{
Box labelBox = new Box(BoxLayout.X_AXIS);
labelBox.add(c);
labelBox.add(Box.createHorizontalGlue());
return labelBox;
}
private Box makeTextBox2(Component c, Component c2)
{
Box labelBox = new Box(BoxLayout.X_AXIS);
labelBox.add(c);
labelBox.add(c2);
labelBox.add(Box.createHorizontalGlue());
return labelBox;
}
private void initFormats()
{
myDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, myLocale);
myDateFormat.setTimeZone(TimeZone.getDefault());
myDateFormat.setLenient(false);
myTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, myLocale);
myTimeFormat.setTimeZone(TimeZone.getDefault());
myTimeFormat.setLenient(false);
}
private String makeDateTimeInfoString(Calendar now)
{
String nowDateString = myDateFormat.format(now.getTime());
String nowTimeString = myTimeFormat.format(now.getTime());
String infoString = " (current date and time is: " + nowDateString
+ " " + nowTimeString + ")";
return infoString;
}
private void updateDateTimeInfoString()
{
Calendar now = Calendar.getInstance(myLocale);
String nowDateAndTimeInfoString = makeDateTimeInfoString(now);
nowDateAndTimeLabel.setText(nowDateAndTimeInfoString);
}
private void createCreateGamesTab()
{
createGamesTab = new Box(BoxLayout.Y_AXIS);
createSettingsPanel();
createGameOptionsPanel();
createTeleportCheckboxPanel();
Box proposeGamePane = new Box(BoxLayout.Y_AXIS);
proposeGamePane.setAlignmentX(Box.CENTER_ALIGNMENT);
proposeGamePane.setBorder(new TitledBorder("Creating games:"));
// proposeGamePane.add(Box.createRigidArea(new Dimension(0, 10)));
proposeGamePane.add(makeTextBox(nonBoldLabel("Set your preferences, "
+ "fill in the 'Summary' text, "
+ "then press 'Propose' to create a game:")));
summaryText = new JTextField(defaultSummaryText);
summaryText.addFocusListener(new FocusAdapter()
{
@Override
public void focusGained(FocusEvent e)
{
summaryText.selectAll();
}
});
proposeGamePane
.add(makeTextBox2(new JLabel("Summary: "), summaryText));
ButtonGroup group = new ButtonGroup();
Box scheduleModes = new Box(BoxLayout.X_AXIS);
// NOTE: the actual radioButtons will be added later, see below
scheduleModes
.add(new JLabel("Choose the when-to-start-type of game: "));
proposeGamePane.add(makeTextBox(scheduleModes));
// The panel with all GUI stuff needed to schedule a game:
Box schedulingPanel = new Box(BoxLayout.Y_AXIS);
schedulingPanel.setAlignmentY(Box.TOP_ALIGNMENT);
Calendar now = Calendar.getInstance(myLocale);
initFormats();
String nowDateAndTimeInfoString = makeDateTimeInfoString(now);
nowDateAndTimeLabel = nonBoldLabel(nowDateAndTimeInfoString);
nowDateAndTimeLabel.setText(nowDateAndTimeInfoString);
schedulingPanel
.add(makeTextBox(nonBoldLabel("Give a start date and time (dd.mm.yyyy and hh:mm) "
+ "and a minimum duration in minutes:")));
// The panel for the actual schedule: date, time and duration fields
Box schedulePanel = new Box(BoxLayout.X_AXIS);
schedulePanel.add(new JLabel("Start at: "));
int days = 0;
int hours = 24;
Calendar defaultStart = getNowPlusOffset(now, days, hours);
String defaultStartDateString = myDateFormat.format(defaultStart
.getTime());
atDateField = new JTextField(defaultStartDateString);
schedulePanel.add(atDateField);
String defaultStartTimeString = myTimeFormat.format(defaultStart
.getTime());
atTimeField = new JTextField(defaultStartTimeString);
schedulePanel.add(atTimeField);
schedulePanel.add(nowDateAndTimeLabel);
schedulePanel.add(Box.createHorizontalGlue());
schedulingPanel.add(schedulePanel);
proposeGamePane.add(schedulingPanel);
Box durationPanel = new Box(BoxLayout.X_AXIS);
durationPanel.add(new JLabel("Duration: "));
durationField = new JTextField("90");
durationPanel.add(durationField);
durationPanel.setAlignmentY(Box.TOP_ALIGNMENT);
// " (the purpose of the duration value is: "
// + " one should only enroll to that game if one knows that one "
// + " will be available for at least that time)"
durationPanel.add(makeTextBox(nonBoldLabel("...")));
durationPanel.add(Box.createHorizontalGlue());
proposeGamePane.add(durationPanel);
proposeGamePane.add(Box.createRigidArea(new Dimension(0, 10)));
// Panel for the propose + cancel buttons, left most field empty:
JPanel pcButtonPane = new JPanel(new GridLayout(0, 3));
proposeGamePane.add(pcButtonPane);
pcButtonPane.add(new JLabel(""));
proposeButton = new JButton(ProposeButtonText);
proposeButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
proposeButtonAction();
}
});
proposeButton.setEnabled(false);
pcButtonPane.add(proposeButton);
cancelButton = new JButton(CancelButtonText);
cancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
cancelButtonAction();
}
});
cancelButton.setEnabled(false);
pcButtonPane.add(cancelButton);
createGamesTab.add(proposeGamePane);
// Panel for the enroll + unenroll buttons:
Box joinGamePane = new Box(BoxLayout.Y_AXIS);
joinGamePane.setBorder(new TitledBorder(
"Joining games someone else has proposed:"));
joinGamePane
.add(makeTextBox(nonBoldLabel("Select a game from the table below, "
+ "and then click enroll to register for that game.")));
JPanel euButtonPane = new JPanel(new GridLayout(0, 3));
euButtonPane.add(new JLabel(""));
enrollButton = new JButton(EnrollButtonText);
enrollButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
enrollButtonAction();
}
});
enrollButton.setEnabled(false);
euButtonPane.add(enrollButton);
unenrollButton = new JButton(UnenrollButtonText);
unenrollButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
unenrollButtonAction();
}
});
unenrollButton.setEnabled(false);
euButtonPane.add(unenrollButton);
joinGamePane.add(euButtonPane);
createGamesTab.add(joinGamePane);
gamesTablesPanel = new JPanel(new BorderLayout());
createGamesTab.add(gamesTablesPanel);
startButton = new JButton(StartButtonText);
startButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
startButtonAction();
}
});
startButton.setEnabled(false);
startLocallyButton = new JButton(StartLocallyButtonText);
startLocallyButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
startLocallyButtonAction();
}
});
startLocallyButton.setEnabled(false);
Box startButtonPane = new Box(BoxLayout.X_AXIS);
startButtonPane.add(startButton);
// startButtonPane.add(startLocallyButton);
startButtonPane.add(Box.createHorizontalGlue());
gamesCards = new JPanel(new CardLayout());
gamesTablesPanel.add(gamesCards, BorderLayout.CENTER);
// Table for proposed games:
propGamesCard = new JPanel(new BorderLayout());
propGamesCard.setBorder(new TitledBorder("Proposed Games"));
propGamesCard.add(nonBoldLabel("The following games are proposed:"),
BorderLayout.NORTH);
proposedGameDataModel = new GameTableModel(myLocale);
proposedGameTable = new ColumnWidthPersistingJTable(
Options.proposedGamesTableOption, options, proposedGameDataModel)
{
//Implement table cell tool tips.
@Override
public String getToolTipText(MouseEvent e)
{
String tip = null;
java.awt.Point p = e.getPoint();
int rowIndex = rowAtPoint(p);
int colIndex = columnAtPoint(p);
int realColumnIndex = convertColumnIndexToModel(colIndex);
// Variant name
switch (realColumnIndex)
{
case 5:
case 6:
case 7:
tip = (String)proposedGameDataModel.getValueAt(
rowIndex, realColumnIndex);
break;
case 9:
tip = proposedGameDataModel
.getOptionsTooltipText(rowIndex);
break;
case 10:
tip = proposedGameDataModel
.getTeleportOptionsTooltipText(rowIndex);
break;
case 15:
tip = "" + getValueAt(rowIndex, colIndex);
break;
default:
tip = super.getToolTipText(e);
}
return tip;
}
};
// Note: This affects globally!
ToolTipManager ttm = ToolTipManager.sharedInstance();
ttm.setInitialDelay(100);
proposedGameTable.getSelectionModel().addListSelectionListener(
new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
updateGUI();
}
});
proposedGameTable
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane propScrollpane = new JScrollPane(proposedGameTable);
propGamesCard.add(propScrollpane, BorderLayout.CENTER);
propGamesCard.add(nonBoldLabel("For some of the longer fields, "
+ "hovering mouse over a column gives more information!"),
BorderLayout.SOUTH);
JPanel dummyCard = new JPanel(new BorderLayout());
dummyCard.add(Box.createRigidArea(new Dimension(0, 50)));
gamesCards.add(dummyCard, "dummy");
gamesCards.add(propGamesCard, CARD_PROPOSED);
CardLayout cl = (CardLayout)gamesCards.getLayout();
cl.show(gamesCards, CARD_PROPOSED);
Box bottomPanel = new Box(BoxLayout.Y_AXIS);
bottomPanel.add(startButtonPane);
infoTextLabel = new JLabel(enrollText);
bottomPanel.add(infoTextLabel);
gamesTablesPanel.add(bottomPanel, BorderLayout.SOUTH);
// Now that we have the scheduling fields and the tables,
// we can add the buttons; because now the fields and tables
// can be enabled or disabled based on "current" (initial) state
// of the radio buttons:
String current = TYPE_INSTANTLY;
ItemListener iListener = new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
reactOnScheduleRadioButtonChange(e);
updateGUI();
}
};
addRadioButton(scheduleModes, group, TYPE_INSTANTLY, current,
iListener);
addRadioButton(scheduleModes, group, TYPE_SCHEDULED, current,
iListener);
}
/**
* Determine a point in time given amount of days and hours from now.
* Round it to a full hour (down if min <= 10, next hour otherwise).
*
* @param days
* @param hours
* @return
*/
private Calendar getNowPlusOffset(Calendar now, int days, int hours)
{
Calendar nowPlusOffset = now;
nowPlusOffset.add(Calendar.DAY_OF_MONTH, days);
nowPlusOffset.add(Calendar.HOUR_OF_DAY, hours);
// Round to full hour: down if <= 10
int min = nowPlusOffset.get(Calendar.MINUTE);
if (min > 10)
{
nowPlusOffset.add(Calendar.HOUR_OF_DAY, 1);
}
nowPlusOffset.set(Calendar.MINUTE, 0);
return nowPlusOffset;
}
public void reactOnScheduleRadioButtonChange(ItemEvent e)
{
if (atDateField == null)
{
// called too early (during creation of buttons)
return;
}
boolean switchToScheduling = false;
Object o = e.getItem();
boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
if (!selected)
{
// ignore the DESELECT - we act based on what was selected.
return;
}
if (o instanceof JRadioButton)
{
JRadioButton b = (JRadioButton)o;
String text = b.getText();
if (text == null)
{
LOGGER.warning("ItemEvent Object Text is null???");
return;
}
if (text.equals(TYPE_SCHEDULED))
{
switchToScheduling = true;
}
else if (text.equals(TYPE_INSTANTLY))
{
switchToScheduling = false;
}
else
{
LOGGER
.warning("ItemEvent Object Text is neither 'scheduled' nor 'instantly'??");
return;
}
}
else
{
LOGGER.warning("ItemEvent Object is not a JRadioButton??");
return;
}
setScheduledGamesMode(switchToScheduling);
}
private void createSettingsPanel()
{
settingsPanel = new JPanel(new GridLayout(0, 2));
settingsPanel.setBorder(new TitledBorder("Game settings"));
// Variant:
String variantName = options.getStringOption(Options.variant);
if (variantName == null || variantName.length() == 0)
{
variantName = Constants.variantArray[0]; // Default variant
}
variantBox = new JComboBox<String>(Constants.variantArray);
variantBox.setSelectedItem(variantName);
variantBox.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String variant = (String)variantBox.getSelectedItem();
options.setOption(Options.variant, variant);
updateMaxSpinner(variant);
}
});
settingsPanel.add(new JLabel("Select variant:"));
settingsPanel.add(variantBox);
// Viewmode:
// String viewmodesArray[] = { "all-public", "auto-tracked", "only-own" };
String viewmodeName = options.getStringOption(Options.viewMode);
if (viewmodeName == null)
{
viewmodeName = Options.viewableAll;
}
viewmodeBox = new JComboBox<String>(Options.viewModeArray);
viewmodeBox.setSelectedItem(viewmodeName);
viewmodeBox.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
options.setOption(Options.viewMode,
(String)viewmodeBox.getSelectedItem());
}
});
settingsPanel.add(new JLabel("Select view mode:"));
settingsPanel.add(viewmodeBox);
// event expiring policy:
String eventExpiringVal = options
.getStringOption(Options.eventExpiring);
if (eventExpiringVal == null)
{
eventExpiringVal = "5";
}
eventExpiringBox = new JComboBox<String>(Options.eventExpiringChoices);
eventExpiringBox.setSelectedItem(eventExpiringVal);
eventExpiringBox.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
options.setOption(Options.eventExpiring,
(String)eventExpiringBox.getSelectedItem());
}
});
settingsPanel.add(new JLabel("Events expire after (turns):"));
settingsPanel.add(eventExpiringBox);
// min, target and max nr. of players:
settingsPanel.add(new JLabel("Select player count preferences:"));
Box playerSelection = new Box(BoxLayout.X_AXIS);
int min = options.getIntOption(Options.minPlayersWeb);
min = (min < 2 || min > 6 ? 2 : min);
int max = options.getIntOption(Options.maxPlayersWeb);
max = (max < min || max > 6 ? 6 : max);
int middle = java.lang.Math.round(((float)min + (float)max) / 2);
int targ = options.getIntOption(Options.targPlayersWeb);
targ = (targ < min || targ > max ? middle : targ);
playerSelection.add(new JLabel("min.:"));
SpinnerNumberModel model = new SpinnerNumberModel(min, 2, 6, 1);
spinner1 = new JSpinner(model);
playerSelection.add(spinner1);
// spinner uses ChangeListener instead of ActionListener.
// Setting them up is quite laborous, and would be called every time
// user modifies it by one. So, we rather query the value then when we
// need it (before exiting (saveOptions) or before propose).
//spinner.addActionListener(this);
playerSelection.add(new JLabel("target.:"));
SpinnerNumberModel model2 = new SpinnerNumberModel(targ, 2, 6, 1);
spinner2 = new JSpinner(model2);
playerSelection.add(spinner2);
playerSelection.add(new JLabel("max.:"));
SpinnerNumberModel model3 = new SpinnerNumberModel(max, 2, 6, 1);
spinner3 = new JSpinner(model3);
playerSelection.add(spinner3);
maxLabel = new JLabel(" max=?");
playerSelection.add(maxLabel);
updateMaxSpinner(variantName);
settingsPanel.add(playerSelection);
createGamesTab.add(settingsPanel);
}
private void createGameOptionsPanel()
{
gameOptionsPanel = new JPanel(new GridLayout(0, 3));
gameOptionsPanel.setBorder(new TitledBorder("Game options"));
for (String optionName : gameOptions)
{
createTpCheckbox(optionName, gameOptionsPanel);
}
/*
Box noAttackBeforePanel = new Box(BoxLayout.X_AXIS);
noAttackBeforePanel.add(new JLabel("No attack before turn: "));
noAttackBeforePanel.add(new JTextField("1", 5));
checkboxPanel.add(noAttackBeforePanel);
*/
createGamesTab.add(gameOptionsPanel);
}
private void createTeleportCheckboxPanel()
{
JPanel tpPanel = new JPanel(new GridLayout(0, 3));
tpPanel.setBorder(new TitledBorder("Teleport options"));
for (String optionName : teleportOptions)
{
createTpCheckbox(optionName, tpPanel);
}
createGamesTab.add(tpPanel);
}
private void createTpCheckbox(final String optionName, Container cpPanel)
{
boolean optionValue = options.getOption(optionName);
final JCheckBox checkbox = new JCheckBox(optionName, optionValue);
checkbox.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
options.setOption(optionName, checkbox.isSelected());
}
});
cpPanel.add(checkbox);
checkboxForOption.put(optionName, checkbox);
}
private void updateMaxSpinner(String variant)
{
SpinnerNumberModel model = (SpinnerNumberModel)spinner3.getModel();
int newMax = getMaxForVariant(variant);
model.setMaximum(new Integer(newMax));
maxLabel.setText(" max=" + newMax);
adjustToPossibleMax(spinner1, newMax);
adjustToPossibleMax(spinner2, newMax);
adjustToPossibleMax(spinner3, newMax);
}
private void adjustToPossibleMax(JSpinner spinner, int max)
{
if (((Integer)spinner.getValue()).intValue() > max)
{
spinner.setValue(new Integer(max));
}
}
private int getMaxForVariant(String variant)
{
int max = 6;
if (variant.equals("Abyssal9"))
{
max = 9;
}
else if (variant.equals("Abyssal3") || variant.equals("SmallTitan"))
{
max = 3;
}
else if (variant.equals("Beelzebub12")
|| variant.equals("BeelzeGods12"))
{
max = 12;
}
else if (variant.equals("ExtTitan"))
{
max = 8;
}
return max;
}
private void createRunningGamesTab()
{
runningGamesTab = new Box(BoxLayout.Y_AXIS);
// ----------------- First the table ---------------------
Box runningGamesPane = new Box(BoxLayout.Y_AXIS);
runningGamesPane.setAlignmentY(0);
runningGamesPane.setBorder(new TitledBorder("Running Games"));
runningGamesPane.add(new JLabel(
"The following games are already running:"));
runGameDataModel = new GameTableModel(myLocale);
runGameTable = new JTable(runGameDataModel);
runGameTable.getSelectionModel().addListSelectionListener(
new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
updateGUI();
}
});
runGameTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane runtablescrollpane = new JScrollPane(runGameTable);
runningGamesPane.add(runtablescrollpane);
runningGamesTab.add(runningGamesPane);
// ------------------ Hide WebClient stuff ---------------
Box joinGamePanel = new Box(BoxLayout.X_AXIS);
joinGamePanel.setBorder(new TitledBorder("Watch an ongoing game"));
watchButton = new JButton(WatchButtonText);
watchButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
watchButtonAction();
}
});
watchButton.setEnabled(false);
watchButton.setAlignmentX(Box.LEFT_ALIGNMENT);
joinGamePanel.add(watchButton);
joinGamePanel.add(Box.createHorizontalGlue());
joinGamePanel.setPreferredSize(joinGamePanel.getMinimumSize());
joinGamePanel.setSize(joinGamePanel.getMinimumSize());
runningGamesTab.add(Box.createVerticalGlue());
runningGamesTab.add(joinGamePanel);
runningGamesTab.add(Box.createRigidArea(new Dimension(0, 20)));
runningGamesTab.add(Box.createVerticalGlue());
Box hideClientPanel = new Box(BoxLayout.Y_AXIS);
hideClientPanel.setBorder(new TitledBorder("Hiding the Web Client"));
hideClientPanel.setAlignmentX(Box.LEFT_ALIGNMENT);
hideButton = new JButton(HideButtonText);
hideButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
hideButtonAction();
}
});
hideButton.setEnabled(false);
hideButton.setAlignmentX(Box.LEFT_ALIGNMENT);
hideClientPanel.add(hideButton);
hideButtonText = new JLabel(CantHideText);
hideClientPanel.add(hideButtonText);
// automatic actions when game starts (client masterboard comes up):
JLabel autoDoLabel = new JLabel("When game starts, automatically:");
autoDoLabel.setAlignmentX(Box.LEFT_ALIGNMENT);
hideClientPanel.add(autoDoLabel);
Box autoDoButtonPane = new Box(BoxLayout.X_AXIS);
autoGSNothingRB = new JRadioButton(AutoGameStartActionNothing);
autoGSHideRB = new JRadioButton(AutoGameStartActionHide);
autoGSCloseRB = new JRadioButton(AutoGameStartActionClose);
autoDoButtonPane.add(autoGSNothingRB);
autoDoButtonPane.add(autoGSHideRB);
autoDoButtonPane.add(autoGSCloseRB);
ActionListener autoGSActionListener = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
options
.setOption(optAutoGameStartAction, e.getActionCommand());
}
};
autoGSNothingRB.addActionListener(autoGSActionListener);
autoGSHideRB.addActionListener(autoGSActionListener);
autoGSCloseRB.addActionListener(autoGSActionListener);
ButtonGroup autoGSActionGroup = new ButtonGroup();
autoGSActionGroup.add(autoGSNothingRB);
autoGSActionGroup.add(autoGSHideRB);
autoGSActionGroup.add(autoGSCloseRB);
String autoGSAction = options.getStringOption(optAutoGameStartAction);
if (autoGSAction == null || autoGSAction.equals(""))
{
autoGSAction = AutoGameStartActionNothing;
options.setOption(optAutoGameStartAction, autoGSAction);
}
if (autoGSAction.equals(AutoGameStartActionNothing))
{
autoGSNothingRB.setSelected(true);
}
else if (autoGSAction.equals(AutoGameStartActionHide))
{
autoGSHideRB.setSelected(true);
}
else if (autoGSAction.equals(AutoGameStartActionClose))
{
autoGSCloseRB.setSelected(true);
}
else
{
autoGSAction = AutoGameStartActionNothing;
options.setOption(optAutoGameStartAction, autoGSAction);
autoGSNothingRB.setSelected(true);
}
autoDoButtonPane.setAlignmentX(Box.LEFT_ALIGNMENT);
hideClientPanel.add(autoDoButtonPane);
hideClientPanel.add(Box.createVerticalGlue());
runningGamesTab.add(Box.createVerticalGlue());
runningGamesTab.add(hideClientPanel);
/* // Somehow this does not work at all...
// as wide as the running games table, as high as needed:
int width = runningGamesPane.getMinimumSize().width;
int height = hideClientPanel.getMinimumSize().height;
Dimension prefSize = new Dimension(width, height);
hideClientPanel.setPreferredSize(prefSize);
hideClientPanel.setMinimumSize(prefSize);
*/
}
private void createSuspendedGamesTab()
{
suspendedGamesTab = new Box(BoxLayout.Y_AXIS);
// ----------------- First the table ---------------------
suspendedGamesTab.setAlignmentY(0);
suspendedGamesTab.setBorder(new TitledBorder("Suspended Games"));
suspendedGamesTab
.add(new JLabel("The following games are suspended:"));
suspGameDataModel = new GameTableModel(myLocale);
suspGameTable = new JTable(suspGameDataModel);
suspGameTable.getSelectionModel().addListSelectionListener(
new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
updateGUI();
}
});
suspGameTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane susptablescrollpane = new JScrollPane(suspGameTable);
suspendedGamesTab.add(susptablescrollpane);
suspendedGamesTab.add(Box.createVerticalGlue());
// ----------------- Resume button -------------------
Box resumeGamePanel = new Box(BoxLayout.X_AXIS);
resumeGamePanel.setBorder(new TitledBorder(
"Resume/Load a suspended game"));
resumeButton = new JButton("Resume Game");
resumeButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
resumeGameButtonAction();
}
});
resumeGamePanel.add(resumeButton);
resumeGamePanel.add(new JLabel(" "));
reasonWhyNotLabel = nonBoldLabel("Not logged in");
resumeGamePanel.add(reasonWhyNotLabel);
resumeGamePanel.add(Box.createHorizontalGlue());
resumeGamePanel.setPreferredSize(resumeGamePanel.getMinimumSize());
resumeGamePanel.setSize(resumeGamePanel.getMinimumSize());
suspendedGamesTab.add(resumeGamePanel);
suspendedGamesTab.add(Box.createVerticalGlue());
// ----------------- Delete button -------------------
Box deletePanel = new Box(BoxLayout.X_AXIS);
deletePanel.setBorder(new TitledBorder("Delete a suspended game"));
deleteButton = new JButton("Delete Game");
deleteButton.setEnabled(true);
deleteButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
deleteButtonAction();
}
});
deletePanel.add(deleteButton);
deletePanel.add(new JLabel(" "));
// reasonWhyNotLabel = nonBoldLabel("Not logged in");
// deletePanel .add(reasonWhyNotLabel);
deletePanel.add(Box.createHorizontalGlue());
deletePanel.setPreferredSize(deletePanel.getMinimumSize());
deletePanel.setSize(deletePanel.getMinimumSize());
/*
* Hardcoded clemens or admin, because at this time we don't have
* the "isAdmin" property set yet.
*
* TODO: do this properly
* (perhaps when we make this available that everybody could delete
* his own games, then the button would be always there and then we
* just need to enable/disable accordingly)
*/
if (username != null
&& (username.equals("clemens") || username.equals("admin")))
{
suspendedGamesTab.add(deletePanel);
suspendedGamesTab.add(Box.createVerticalGlue());
}
// ----------------- Recover button -------------------
Box recoverGamePanel = new Box(BoxLayout.X_AXIS);
recoverGamePanel.setBorder(new TitledBorder("Recover a crashed game"));
recoverButton = new JButton("Browse");
recoverButton.setEnabled(false);
recoverButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
recoverGame();
}
});
recoverGamePanel.add(recoverButton);
JLabel niLabel = new JLabel(" (not implemented yet)");
niLabel.setFont(niLabel.getFont().deriveFont(Font.ITALIC));
recoverGamePanel.add(niLabel);
recoverGamePanel.add(Box.createHorizontalGlue());
recoverGamePanel.setPreferredSize(recoverGamePanel.getMinimumSize());
recoverGamePanel.setSize(recoverGamePanel.getMinimumSize());
suspendedGamesTab.add(recoverGamePanel);
suspendedGamesTab.add(Box.createVerticalGlue());
}
private void recoverGame()
{
new RecoverGameDialog(this, options);
}
private void createAdminTab()
{
adminTab = new Box(BoxLayout.Y_AXIS);
JPanel adminPane = new JPanel(new GridLayout(0, 1));
adminPane.setBorder(new TitledBorder("Admin mode"));
adminPane.setPreferredSize(new Dimension(30, 200));
commandField = new JTextField("");
adminPane.add(commandField);
debugSubmitButton = new JButton("Submit");
debugSubmitButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
debugSubmitButtonAction();
}
});
debugSubmitButton.setEnabled(false);
adminPane.add(debugSubmitButton);
adminPane.add(new JLabel("Server answered:"));
receivedField = new JLabel("");
adminPane.add(receivedField);
rereadLoginMsgButton = new JButton("Reread Login Message");
rereadLoginMsgButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
rereadLoginMsgButtonAction();
}
});
adminPane.add(rereadLoginMsgButton);
shutdownButton = new JButton("Shutdown Server");
shutdownButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
shutdownButtonAction();
}
});
adminPane.add(shutdownButton);
dumpInfoButton = new JButton("Dump Info on Server");
dumpInfoButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dumpInfoButtonAction();
}
});
adminPane.add(dumpInfoButton);
notifyMessageField = new JTextField();
adminPane.add(notifyMessageField);
Box notifyPane = new Box(BoxLayout.X_AXIS);
notifyPane.add(new JLabel("User: "));
notifyUserField = new JTextField();
notifyPane.add(notifyUserField);
notifyPane.add(new JLabel("beep count: "));
beepCountField = new JTextField();
notifyPane.add(beepCountField);
notifyPane.add(new JLabel("beep interval (ms): "));
beepIntervalField = new JTextField();
notifyPane.add(beepIntervalField);
JButton beepButton = new JButton("Beep");
beepButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
beepButtonAction();
}
});
notifyPane.add(beepButton);
adminPane.add(notifyPane);
adminTab.add(adminPane);
}
public void beepButtonAction()
{
if (isAdmin())
{
long when = new Date().getTime();
String sender = username;
boolean isAdmin = true;
String recipient = notifyUserField.getText();
String message = notifyMessageField.getText();
int beepCount = Integer.parseInt(beepCountField.getText());
long beepInterval = Long.parseLong(beepIntervalField.getText());
boolean windows = true;
server.requestUserAttention(when, sender, isAdmin, recipient,
message, beepCount, beepInterval, windows);
}
}
public String createLoginWebClientSocketThread(boolean force)
{
String reason = null;
failedDueToDuplicateLogin = false;
failedDueToOwnCancel = false;
updateStatus("Trying to connect...", Color.blue);
loginLogoutButton.setText(CancelLoginButtonText);
// email is null: WCST does login
wcst = new WebClientSocketThread(this, hostname, port, username,
password, force, null, null, gameHash);
WcstException e = wcst.getException();
if (e == null)
{
wcst.start();
server = wcst;
updateStatus("Logged in", Color.green);
}
else
{
loginLogoutButton.setText(LoginButtonText);
// I would have liked to let the constructor throw an exception
// and catch this here, but then the returned pointer was null,
// so could not do anything with it (and start() not be run),
// so GC did not clean it up. Sooo... let's do it this way,
// a little bit clumsy...
if (wcst.stillNeedsRun())
{
wcst.start();
}
wcst = null;
server = null;
reason = e.getMessage();
if (reason == null)
{
reason = "Unknown reason";
}
// if it is the duplicate login case: if force was not set, give
// user a 2nd chance to login with force, without the original message.
if (!force && e.failedBecauseAlreadyLoggedIn())
{
failedDueToDuplicateLogin = true;
return reason;
}
if (e.wasCancelled())
{
failedDueToOwnCancel = true;
return reason;
}
// otherwise just show what's wrong
JOptionPane.showMessageDialog(this, reason);
updateStatus("Login failed", Color.red);
return reason;
}
return reason;
}
public String createRegisterWebClientSocketThread(String username,
String password, String email, String confCode)
{
LOGGER.info("Creating a RegisterWCST, username " + username
+ " password " + password + " and confcode " + confCode);
String reason = null;
boolean force = false; // dummy
// 1) confCode is not null: WCST does the confirmation
// 2) email is NOT null: WCST does register first instead
// 3) otherwise: normal login
wcst = new WebClientSocketThread(this, hostname, port, username,
password, force, email, confCode, gameHash);
WcstException e = wcst.getException();
if (e == null)
{
LOGGER.info("Account for user" + username
+ " created successfully!");
wcst.start();
server = wcst;
updateStatus("Successfully registered", Color.green);
JOptionPane.showMessageDialog(registerPanel,
"Account was created successfully!\nYou can Login now.",
"Registration OK", JOptionPane.INFORMATION_MESSAGE);
loginField.setText(username);
passwordField.setText(password);
WebClient.this.login = username;
WebClient.this.username = username;
WebClient.this.password = password;
registerPanel.dispose();
}
else
{
reason = e.getMessage();
if (reason == null)
{
reason = "Unknown reason";
}
if (reason.equals(User.PROVIDE_CONFCODE))
{
LOGGER.info("As expected, server asks us now for "
+ "the confirmation code for user " + username);
}
else
{
LOGGER.info("Failed to create account for user " + username
+ "; reason: '" + reason + "'");
}
// Register password panel handles this from here on,
// namely let the user input the confirmation code and
// submitting it.
}
return reason;
}
private void logout()
{
// When watching a game and it ended with Draw, when I did logout
// the server.logout() caused a NPE ... prevent it, just in case.
if (server != null)
{
server.logout();
server = null;
wcst = null;
}
updateStatus("Not connected", Color.red);
return;
}
private void doQuit()
{
if (gameClient != null)
{
// Game client handles confirmation if necessary,
// asks what to do next, and sets startObj accordingly,
// and it also disposes this WebClient window.
gameClient.getGUI().doConfirmAndQuit();
}
else
{
whatNextManager.setWhatToDoNext(WhatToDoNext.QUIT_ALL, true);
dispose();
}
}
@Override
public void dispose()
{
// we have a server ( = a WebClientSocketThread)
// if and only if we are logged in.
if (server != null)
{
doLogout();
}
super.dispose();
int min = ((Integer)spinner1.getValue()).intValue();
int target = ((Integer)spinner2.getValue()).intValue();
int max = ((Integer)spinner3.getValue()).intValue();
options.setOption(Options.minPlayersWeb, min);
options.setOption(Options.maxPlayersWeb, max);
options.setOption(Options.targPlayersWeb, target);
// options.setStringOption(Options.)
options.saveOptions();
if (gameClient != null)
{
gameClient.getGUI().clearWebClient();
gameClient = null;
}
ViableEntityManager.unregister(this);
}
private String getUserinfoText()
{
String text;
if (state == NotLoggedIn)
{
text = "<unknown>";
}
else if (usersLoggedIn <= 1)
{
text = "No other users logged in.";
}
else
{
text = usersLoggedIn + " logged in (" + usersText + ").";
// just to get rid of the "never read locally" warning...:
String dummy = (usersEnrolled + usersPlaying + usersDead + usersLogoffAgo)
+ usersText;
LOGGER.log(Level.FINEST, "Loggedin: " + usersLoggedIn
+ ", others dummy: " + dummy);
// // Server doesn't tell actual values for the other stuff yet.
// text = usersLoggedIn + " logged in, of that " +
// usersEnrolled + " enrolled, " +
// usersPlaying + "playing and " +
// usersDead + "playing but eliminated.";
}
return text;
}
public void updateGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
doUpdateGUI();
}
});
}
public String getSelectedGameIdFromProposedTable()
{
return getSelectedGameId(proposedGameTable);
}
public String getSelectedGameIdFromRunTable()
{
return getSelectedGameId(runGameTable);
}
public String getSelectedGameIdFromSuspTable()
{
return getSelectedGameId(suspGameTable);
}
public String getSelectedGameId(JTable table)
{
String id = null;
int selRow = table.getSelectedRow();
if (selRow != -1)
{
id = (String)table.getValueAt(selRow, 0);
if (!gameHash.containsKey(id))
{
LOGGER.warning("Game with id " + id
+ " is not in game hash any more...");
return null;
}
}
return id;
}
private String makeWindowTitleForState(int state)
{
switch (state)
{
case NotLoggedIn:
return "(not logged in)";
case LoggedIn:
return username + " (logged in)";
case EnrolledInstantGame:
return username + " (enrolled)";
case Playing:
return username + " (playing)";
case Watching:
return username + " (watching)";
default:
return "<window title - undefined state?>";
}
}
private String makeInfoTextForState(int state)
{
switch (state)
{
case NotLoggedIn:
return needLoginText;
case LoggedIn:
return enrollText;
case EnrolledInstantGame:
return enrolledText;
case Playing:
return playingText;
case Watching:
return watchingText;
default:
return "<info text - undefined state?>";
}
}
// TODO mostly duplicate with makeWindowTitleForState
private String makeStatusTextForState(int state)
{
switch (state)
{
case NotLoggedIn:
return "not logged in";
case EnrolledInstantGame:
return "As " + username + " - enrolled to instant game "
+ enrolledInstantGameId;
case Playing:
return "As " + username + " - playing game #" + getOngoingGameId();
case Watching:
return "As " + username + " - watching game #"
+ watchingInstantGameId;
case LoggedIn:
return "logged in as " + username;
default:
return "<info text - undefined state?>";
}
}
/**
* Returns true if this user would be allowed to start this game
* (given that all other conditions are fulfilled).
* Usually the allowed player is the one who created it, but if
* that one is not enrolled, the first of the enrolled ones is
* allowed then to do it.
*
* @param gi
* @return Whether this player would be allowed to start this game
*/
private boolean isEligibleToStart(GameInfo gi)
{
if (gi.getInitiator().equals(username))
{
return true;
}
if (!gi.isEnrolled(gi.getInitiator())
&& gi.isFirstInEnrolledList(username))
{
return true;
}
return false;
}
private boolean checkIfCouldWatch(int state)
{
switch (state)
{
case NotLoggedIn:
case EnrolledInstantGame:
case Playing:
case Watching:
return false;
case LoggedIn:
default:
boolean watchPossible = false;
String id = getSelectedGameIdFromRunTable();
if (id != null)
{
watchPossible = true;
}
return watchPossible;
}
}
private String checkIfCouldResume(int state)
{
boolean DEBUG = false;
if (DEBUG)
{
return null;
}
String reasonWhyNot = null;
switch (state)
{
case NotLoggedIn:
case EnrolledInstantGame:
case Playing:
case Watching:
return "Can't resume while enrolled, playing or watching another game.";
case LoggedIn:
default:
if (gameResumeInitiated)
{
return "Resume of suspended game is ongoing";
}
if (startingGame != null)
{
return "Resume of suspended game is ongoing";
}
if (suspGameDataModel.getRowCount() == 0)
{
return "You don't have any suspended games.";
}
reasonWhyNot = "You are not enrolled into this game.";
String id = getSelectedGameIdFromSuspTable();
if (id != null)
{
GameInfo gi = findGameById(id);
if (gi.getOnlineCount() < 2)
{
reasonWhyNot = "No other player online for game #"
+ gi.getGameId();
}
else if (gi.getOnlineCount() != gi.getPlayers().size())
{
reasonWhyNot = PLAYERS_MISSING;
}
else if (gi.isEnrolled(username) || isAdmin)
{
reasonWhyNot = null;
}
// else case defaults to 'you are not enrolled'
}
else
{
reasonWhyNot = "Select a game in the table.";
}
return reasonWhyNot;
}
}
private boolean checkIfCouldStartOnServer(int state)
{
switch (state)
{
case EnrolledInstantGame:
GameInfo gi = findGameById(enrolledInstantGameId);
if (gi != null)
{
if (gi.enoughPlayersEnrolled() && gi.allEnrolledOnline()
&& gi.isStartable() && isEligibleToStart(gi))
{
return true;
}
else
{
return false;
}
}
else
{
LOGGER.warning("Huuuh? UpdateGUI, Enrolled, but get game "
+ "from hash for enrolledGameId is null??");
}
return false;
case LoggedIn:
boolean startPossible = false;
String id = getSelectedGameIdFromProposedTable();
if (id != null && isScheduledGameAndStartable(id))
{
startPossible = true;
}
return startPossible;
case NotLoggedIn:
case Playing:
case Watching:
default:
return false;
}
}
private boolean isScheduledGameAndStartable(String id)
{
assert id != null : "Not a valid game id: " + id;
GameInfo gi = findGameById(id);
if (gi == null)
{
return false;
}
if (gi.isStartable() && gi.isEnrolled(username)
&& isEligibleToStart(gi) && gi.isScheduledGame() && gi.isDue()
&& gi.hasEnoughPlayers() && gi.allEnrolledOnline())
{
return true;
}
else
{
return false;
}
}
private boolean checkIfCouldPropose()
{
if (state == NotLoggedIn)
{
return false;
}
boolean scheduled = getScheduledGamesMode();
if (!scheduled && (state == Playing || state == Watching))
{
return false;
}
return true;
}
private boolean checkIfCouldCancel()
{
if (state == NotLoggedIn)
{
return false;
}
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId != null && (isOwner(selectedGameId) || isAdmin()))
{
return true;
}
else
{
return false;
}
}
private boolean checkIfCouldEnroll()
{
if (state == NotLoggedIn || state == Playing || state == Watching)
{
return false;
}
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId == null)
{
return false;
}
GameInfo gi = findGameById(selectedGameId);
// only if not enrolled yet, and still in PROPOSED or DUE state
if (gi != null && !gi.isEnrolled(username) && gi.isStartable())
{
return true;
}
else
{
return false;
}
}
private boolean checkIfCouldUnenroll()
{
if (state == NotLoggedIn)
{
return false;
}
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId == null)
{
return false;
}
GameInfo gi = findGameById(selectedGameId);
if (gi != null && gi.isEnrolled(username))
{
return true;
}
else
{
return false;
}
}
private boolean checkIfCouldContactAdmin()
{
return (state != NotLoggedIn);
}
// this should always be called inside a invokeLater (i.e. in EDT)!!
public void doUpdateGUI()
{
// =============================================================
// First calculate new state for all kind of things...
String newTitle = makeWindowTitleForState(state);
String newInfoText;
if (startingGame != null)
{
newInfoText = "Game was started by user "
+ startingGame.getStartingUser().getName()
+ "; MasterBoard should appear soon. Please wait...";
}
else
{
newInfoText = makeInfoTextForState(state);
}
String newStatusText = makeStatusTextForState(state);
boolean couldPropose = checkIfCouldPropose();
boolean couldDebugSubmit = (state == LoggedIn);
boolean couldCancel = checkIfCouldCancel();
boolean couldEnroll = checkIfCouldEnroll();
boolean couldUnenroll = checkIfCouldUnenroll();
boolean couldStartOnServer = checkIfCouldStartOnServer(state);
// feature currently disabled (( => hardcoded to false)):
boolean couldStartLocally = false;
boolean couldWatch = checkIfCouldWatch(state);
String reasonWhyCantResume = checkIfCouldResume(state);
boolean couldContactAdmin = checkIfCouldContactAdmin();
// ----------------------------------------------------------------
// ... and now actually change the GUI
if (!newTitle.equals(""))
{
this.setTitle(windowTitle + " " + newTitle);
}
// Server tab
loginLogoutButton.setText(state != NotLoggedIn ? LogoutButtonText
: LoginButtonText);
if (state != NotLoggedIn)
{
registerOrPasswordLabel.setText(chgPasswordLabelText);
registerOrPasswordButton.setText(chgPasswordButtonText);
}
else
{
registerOrPasswordLabel.setText(createAccountLabelText);
registerOrPasswordButton.setText(createAccountButtonText);
}
// Games tab
updateDateTimeInfoString();
userinfoLabel.setText("Userinfo: " + getUserinfoText());
statusLabel.setText("Status: " + newStatusText);
infoTextLabel.setText(newInfoText);
contactAdminButton.setEnabled(couldContactAdmin);
proposeButton.setEnabled(couldPropose);
cancelButton.setEnabled(couldCancel);
enrollButton.setEnabled(couldEnroll);
unenrollButton.setEnabled(couldUnenroll);
startButton.setEnabled(couldStartOnServer);
startLocallyButton.setEnabled(couldStartLocally);
watchButton.setEnabled(couldWatch);
resumeButton.setEnabled(reasonWhyCantResume == null
|| reasonWhyCantResume.equals(PLAYERS_MISSING));
String text;
if (reasonWhyCantResume == null)
{
text = "Click to resume the game game #"
+ getSelectedGameIdFromSuspTable() + "!";
}
else if (reasonWhyCantResume.equals(PLAYERS_MISSING))
{
text = "NOTE: not all players online. Resume will succeed "
+ "only if missing players are dead!";
}
else
{
text = reasonWhyCantResume;
}
reasonWhyNotLabel.setText(text);
// Chat tab
generalChat.setLoginState(state != NotLoggedIn, server, username);
// Admin tab
debugSubmitButton.setEnabled(couldDebugSubmit);
}
// SocketThread needs this to find games when "reinstantiating" it
// from tokens got from server
public HashMap<String, GameInfo> getGameHash()
{
return gameHash;
}
private GameInfo findGameByIdNoComplaint(String gameId)
{
GameInfo gi = gameHash.get(gameId);
return gi;
}
private GameInfo findGameById(String gameId)
{
GameInfo gi = gameHash.get(gameId);
if (gi == null)
{
LOGGER.log(Level.SEVERE, "Game from hash for gameId " + gameId
+ " is null!!");
Thread.dumpStack();
}
return gi;
}
private boolean isOwner(String gameId)
{
GameInfo gi = findGameById(gameId);
if (gi == null)
{
return false;
}
String initiator = gi.getInitiator();
return (username.equals(initiator));
}
/* Validate that the given field does not contain any substring which
* could cause a wrong splitting it by separator at the recipients side.
*
* As the separator currently is " ~ ", in practice this means
* the userName and password must not start or end with whitespaces
* or the '~' character, nor contain the separator as a whole.
*
* If invalid, displays a message box telling what is wrong and returns
* false; if valid, returns true.
*/
public boolean validateField(Component parent, String content,
String fieldName)
{
String problem = null;
String temp = content.trim();
if (!temp.equals(content))
{
problem = fieldName + " must not start or end with whitespaces!";
}
else if (temp.equalsIgnoreCase(""))
{
problem = fieldName + " is missing!";
}
else if (temp.equalsIgnoreCase("null"))
{
problem = fieldName
+ " must not be the string 'null', no matter which case!";
}
else if (content.indexOf(sep) != -1)
{
problem = fieldName + " must not contain the string '" + sep
+ "'!";
}
else
{
for (int i = 0; i < sep.length() && problem == null; i++)
{
String critChar = sep.substring(i, i + 1);
if (content.startsWith(critChar))
{
problem = fieldName + " must not start with '" + critChar
+ "'!";
}
else if (content.endsWith(critChar))
{
problem = fieldName + " must not end with '" + critChar
+ "'!";
}
}
}
if (problem != null)
{
JOptionPane.showMessageDialog(parent, problem);
return false;
}
return true;
}
boolean validatePort(Component parent, String portText)
{
boolean ok = true;
int port = -1;
try
{
port = Integer.parseInt(portText);
if (port < minPort || port > maxPort)
{
ok = false;
}
}
catch (Exception e)
{
ok = false;
}
if (!ok)
{
JOptionPane.showMessageDialog(parent, "Invalid port number!");
}
return ok;
}
public void doLogin()
{
boolean ok = validateServerAndPort();
ok = ok
&& validateField(this, loginField.getText(), "Login name")
&& validateField(this, new String(passwordField.getPassword()),
"Password");
if (!ok)
{
return;
}
this.login = loginField.getText();
this.username = this.login;
this.password = new String(passwordField.getPassword());
// first try without force
String message = createLoginWebClientSocketThread(false);
if (message != null && failedDueToDuplicateLogin)
{
Object[] options = { "Force", "Cancel" };
int answer = JOptionPane.showOptionDialog(this,
"Server has already/still another connection open with "
+ "that login name. Click Force to forcefully logout the "
+ "other connection, or Cancel to abort.",
"WebClient login: Force logout of other connection?",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
null, options, options[1]);
if (answer == 0)
{
// if user clicked 'Force', try to connect/login with force argument
message = createLoginWebClientSocketThread(true);
}
}
else if (message != null && failedDueToOwnCancel)
{
state = NotLoggedIn;
loginField.setEnabled(true);
updateStatus("Login attempt cancelled... - Not logged in",
Color.BLUE);
updateGUI();
}
if (message == null)
{
enrolledInstantGameId = null;
watchingInstantGameId = null;
state = LoggedIn;
loginField.setEnabled(false);
updateGUI();
options.setOption(Options.webServerHost, this.hostname);
options.setOption(Options.webServerPort, this.port);
options.setOption(Options.webClientLogin, this.login);
options.setOption(Options.webClientPassword, this.password);
options.saveOptions();
if (options.getOption(WebClient.AutoGamePaneCBText))
{
tabbedPane.setSelectedComponent(createGamesTab);
}
}
else
{
LOGGER.log(Level.FINEST, "connect/login failed...");
}
}
public void doCancelConnect()
{
WebClientSocketThread.cancelConnectAttempt();
}
public boolean validateServerAndPort()
{
String portText = webserverPortField.getText();
boolean ok = validatePort(this, portText)
&& validateField(this, webserverHostField.getText(), "Host name");
if (ok)
{
this.port = Integer.parseInt(webserverPortField.getText());
this.hostname = webserverHostField.getText();
}
return ok;
}
public void doLogout()
{
/*
* Commented out because it caused a problem: if the gameInfo update
* from server both for unenroll and cancel both go the gameUpdates
* list, it sometimes happened that it re-added it to the table again.
*/
/*
if (state == EnrolledInstantGame)
{
if (enrolledInstantGameId != null)
{
doUnenroll(enrolledInstantGameId);
}
else
{
LOGGER.warning("state enrolledInstantGame but id is null?");
}
}
*/
cancelOwnInstantGameOnLogout();
logout();
synchronized (gamesUpdates)
{
gamesUpdates.clear();
}
proposedGameDataModel.resetTable();
runGameDataModel.resetTable();
suspGameDataModel.resetTable();
gameHash.clear();
state = NotLoggedIn;
setAdmin(false);
loginField.setEnabled(true);
updateGUI();
options.setOption(Options.webClientLogin, this.login);
options.setOption(Options.webClientPassword, this.password);
options.saveOptions();
}
private void cancelOwnInstantGameOnLogout()
{
for (GameInfo gi : findMyInstantGames())
{
server.cancelGame(gi.getGameId(), username);
}
}
private void doRegisterOrPasswordDialog(boolean register)
{
boolean ok = validateServerAndPort();
if (ok)
{
String username = loginField.getText();
registerPanel = new RegisterPasswordPanel(this, register, username);
registerPanel.packAndShow();
}
}
public String tryChangePassword(String name, String oldPW, String newPW1)
{
// email and isAdminObj are null, this signals: do not change them
String email = null;
Boolean isAdminObj = null;
String reason = server.changeProperties(name, oldPW, newPW1, email,
isAdminObj);
if (reason == null || reason.equals("null"))
{
passwordField.setText(newPW1);
password = newPW1;
// all went fine, panel shows ok.
return null;
}
else
{
// panel shows failure message and reason
return reason;
}
}
private void doCancel(String gameId)
{
server.cancelGame(gameId, username);
updateGUI();
}
public void doScheduleDummy()
{
// just a dummy as long as we still have ScheduledGamesTab class...
}
private void do_proposeGame(String variant, String viewmode, long startAt,
int duration, String summary, String expire, List<String> gameOptions,
List<String> teleportOptions, int min, int target, int max)
{
server.proposeGame(username, variant, viewmode, startAt, duration,
summary, expire, gameOptions, teleportOptions, min, target, max);
}
private long getStartTime()
{
long when = -1;
String atDate = atDateField.getText();
String atTime = atTimeField.getText();
String schedule = atDate + " " + atTime;
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.SHORT, myLocale);
df.setTimeZone(TimeZone.getDefault());
df.setLenient(false);
try
{
Date whenDate = df.parse(schedule);
when = whenDate.getTime();
}
catch (ParseException e)
{
LOGGER.warning("Illegal date/time '" + schedule + "'");
}
return when;
}
private int getDuration()
{
int duration = -1;
String durationString = durationField.getText();
duration = Integer.parseInt(durationString);
return duration;
}
private String getSummaryText()
{
return summaryText.getText();
}
private boolean doEnroll(String gameId)
{
server.enrollUserToGame(gameId, username);
return true;
}
private boolean doUnenroll(String gameId)
{
server.unenrollUserFromGame(gameId, username);
return true;
}
/**
* Called when user presses the "Start" button in "Create or Join" tab
* and then sends the necessary message to server
* @param gameId
* @return if things went OK or not (ATM always true)
*/
boolean doStart(String gameId)
{
startButton.setEnabled(false);
startLocallyButton.setEnabled(false);
cancelButton.setEnabled(false);
unenrollButton.setEnabled(false);
// TODO better handle with changing state, but not today...
infoTextLabel.setText(startClickedText);
server.startGame(gameId, new User(username));
return true;
}
/**
* Called when user presses the "Resume" button in "Suspended Games" tab
* to send the necessary message to server
* @param gameId
* @return if things went OK or not (ATM always true)
*/
boolean doResume(final String gameId)
{
String filename = "dummy";
server.resumeGame(gameId, filename, new User(username));
return true;
}
/**
* Called when user presses the "Delete" button in "Suspended Games" tab
* to send the necessary message to server
*
* TODO: minimal functionality to be used by admin, perhaps this does not
* completely all update GUI stuff...
* @param gameId
* @return if things went OK or not (ATM always true)
*/
boolean doDeleteSuspendedGame(String gameId)
{
server.deleteSuspendedGame(gameId, new User(username));
return true;
}
// Called when user presses the "Start Locally" button in
// "Create or Join" tab
// TODO: Dead functionality!!
private boolean doStartLocally(String gameId)
{
startButton.setEnabled(false);
startLocallyButton.setEnabled(false);
boolean ok = true;
GameInfo gi = findGameById(gameId);
gameRunner = new RunGameInSameJVM(gi, whatNextManager, username, this);
gameRunner.start();
return ok;
}
public void informStartingOnPlayerHost(String hostingPlayer,
String hostingHost, int hostingPort)
{
server.startGameOnPlayerHost(getSelectedGameIdFromProposedTable(), hostingPlayer,
hostingHost, hostingPort);
}
public void informGameStartedLocally()
{
server.informStartedByPlayer(this.startedGameId);
}
public void informLocallyGameOver()
{
server.informLocallyGameOver(this.startedGameId);
}
public void setLocalServer(Server server)
{
localServer = server;
}
// ================= those come from server ============
public void grantAdminStatus()
{
setAdmin(true);
}
public void didEnroll(String gameId, String user)
{
GameInfo gi = findGameById(gameId);
if (gi.getGameState().equals(GameState.SUSPENDED))
{
return;
}
boolean scheduled = gi.isScheduledGame();
int index = proposedGameDataModel.getRowIndex(gi).intValue();
proposedGameTable.setRowSelectionInterval(index, index);
if (!scheduled)
{
state = EnrolledInstantGame;
enrolledInstantGameId = gameId;
}
updateGUI();
}
public void didUnenroll(String gameId, String user)
{
// do not set it back to LoggedIn if this didUnenroll is the
// result of a automatic unenroll before logout
if (watchingInstantGameId != null)
{
// should never happen, in any case prevent it from setting state
// to plain "LoggedIn"
}
else if (state != NotLoggedIn)
{
state = LoggedIn;
}
enrolledInstantGameId = null;
updateGUI();
}
public void setWatching(String gameId)
{
watchingInstantGameId = gameId;
state = Watching;
}
public void clearWatching()
{
watchingInstantGameId = null;
state = LoggedIn;
}
public void gameStartsSoon(String gameId, String startUser)
{
LOGGER.fine("Game starts soon, gameid " + gameId);
GameInfo gi = findGameById(gameId);
if (gi != null)
{
gi.markStarting(new User(startUser));
gameResumeInitiated = false;
startingGame = gi;
}
updateGUI();
}
// Client calls this
public void notifyComingUp(boolean success)
{
synchronized (comingUpMutex)
{
// skip if timer already did it (perhaps shortly ago)
if (!timeIsUp)
{
if (success)
{
clientIsUp = true;
}
else
{
clientStartFailed = true;
}
comingUpMutex.notify();
}
}
}
public void notifyGameSuspended()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JOptionPane
.showMessageDialog(
WebClient.this,
"Your board was closed because the game was suspended.\n\n"
+ "The game can be resumed in 'Suspended Games' tab,\n"
+ "now or also later.", "Game suspended",
JOptionPane.INFORMATION_MESSAGE);
state = LoggedIn;
enrolledInstantGameId = null;
updateGUI();
}
});
}
private Timer setupTimer()
{
// java.util.Timer, not Swing Timer
Timer timer = new Timer();
timeIsUp = false;
clientStartFailed = false;
clientIsUp = false;
long timeout = 60; // secs
timer.schedule(new TriggerTimeIsUp(), timeout * 1000);
return timer;
}
class TriggerTimeIsUp extends TimerTask
{
@Override
public void run()
{
synchronized (comingUpMutex)
{
if (clientIsUp || clientStartFailed)
{
// skip, timer was triggered just when start succeeded
}
else
{
timeIsUp = true;
comingUpMutex.notify();
}
}
}
}
public void gameStartsNow(String gameId, int port, String hostingHost,
final int inactivityCheckInterval,
final int inactivityWarningInterval, final int inactivityTimeout)
{
LOGGER.info("Game starts now, gameid " + gameId);
if (hostingHost == null || hostingHost.equals("null"))
{
// Hosted on Game Server
hostingHost = hostname;
}
// For now, just always use runnable (that's why the "&& IN_USE" which evals to false)
// TODO Is the runnable necessary?
boolean IN_USE = false;
if (startedGameId == null && IN_USE)
{
// is null means: it was not this webclient that started locally
startOwnClient(gameId, port, hostingHost,
inactivityWarningInterval);
}
// This WebClient did start it...
else
{
// ... then we need to start the Client in own runnable,
// otherwise we (in WebClientSocketThread) will not be
// back at the socket to receive the "Game is now up" message
startedAtPort = port;
startedAtHost = hostingHost;
startedGameId = gameId;
Runnable r = new Runnable()
{
public void run()
{
String gameId = WebClient.this.startedGameId;
int port = WebClient.this.startedAtPort;
String host = WebClient.this.startedAtHost;
startOwnClient(gameId, port, host,
inactivityWarningInterval);
}
};
new Thread(r).start();
}
}
public void startOwnClient(String gameId, int port, String hostingHost,
int inactivityWarningInterval)
{
LOGGER.info("StartingOwnClient for gameId " + gameId + " hostingHost "
+ hostingHost + " port " + port);
Client gc;
try
{
int p = port;
String type;
if (hostingHost == null || hostingHost.equals(""))
{
// Game runs on WebServer
hostingHost = hostname;
type = Constants.network;
}
else
{
// Runs on a players computer
type = Constants.human;
}
boolean noOptionsFile = false;
gc = Client
.createClient(hostingHost, p, username, type, whatNextManager,
localServer, true, noOptionsFile, true, false);
// Right now this waitingText is probably directly overwritten by
// updateGUI putting there again the startingText (started by player ...)
// but I don't want to fix even that still today...
// TODO make this behave properly...
infoTextLabel.setText(waitingText);
setGameClient(gc);
gc.getGUI().setWebClient(this, inactivityWarningInterval, gameId,
username, password);
Timer timeoutStartup = setupTimer();
while (!clientIsUp && !timeIsUp && !clientStartFailed)
{
synchronized (comingUpMutex)
{
try
{
comingUpMutex.wait();
}
catch (InterruptedException e)
{
// ignored
}
catch (Exception e)
{
// just to be sure...
}
}
}
timeoutStartup.cancel();
startingGame = null;
if (clientIsUp)
{
state = Playing;
updateGUI();
onGameStartAutoAction();
}
else
{
JOptionPane.showMessageDialog(this,
"Own client could connect, but game did not start "
+ "(probably some other player connecting failed?)",
"Starting game failed!", JOptionPane.ERROR_MESSAGE);
state = LoggedIn;
enrolledInstantGameId = null;
updateGUI();
}
}
catch (ConnectionInitException e)
{
gc = null;
JOptionPane.showMessageDialog(this,
"Connecting to the game server hosting the game ("
+ hostingHost + ") or starting own MasterBoard failed!\n"
+ "Reason: " + e.getMessage(), "Starting game failed!",
JOptionPane.ERROR_MESSAGE);
state = LoggedIn;
updateGUI();
}
catch (Exception e)
{
gc = null;
// client startup failed for some reason
JOptionPane.showMessageDialog(
this,
"Unexpected exception while starting the game client: "
+ e.toString(), "Starting game failed!",
JOptionPane.ERROR_MESSAGE);
state = LoggedIn;
updateGUI();
}
}
public void startSpectatorClient(String gameId, int port,
String hostingHost)
{
LOGGER.info("Starting Spectator Client for gameId " + gameId
+ " hostingHost " + hostingHost + " port " + port);
Client gc;
try
{
int p = port;
String type;
if (hostingHost == null || hostingHost.equals(""))
{
// Game runs on WebServer
hostingHost = hostname;
type = Constants.network;
}
else
{
// Runs on a players computer
type = Constants.human;
}
boolean noOptionsFile = false;
gc = Client.createClient(hostingHost, p, username, type,
whatNextManager, localServer, true, noOptionsFile, true, true);
// Right now this waitingText is probably directly overwritten by
// updateGUI putting there again the startingText (started by player ...)
// but I don't want to fix even that still today...
// TODO make this behave properly...
infoTextLabel.setText(waitingText);
setGameClient(gc);
gc.getGUI().setWebClient(this, -1, gameId, username, password);
Timer timeoutStartup = setupTimer();
while (!clientIsUp && !timeIsUp && !clientStartFailed)
{
synchronized (comingUpMutex)
{
try
{
comingUpMutex.wait();
}
catch (InterruptedException e)
{
// ignored
}
catch (Exception e)
{
// just to be sure...
}
}
}
timeoutStartup.cancel();
startingGame = null;
if (clientIsUp)
{
setWatching(gameId);
updateGUI();
// onGameStartAutoAction();
}
else
{
JOptionPane.showMessageDialog(this,
"Trouble when connecting to game to watch?",
"Watching game failed!", JOptionPane.ERROR_MESSAGE);
clearWatching();
updateGUI();
}
}
catch (ConnectionInitException e)
{
gc = null;
JOptionPane.showMessageDialog(this,
"Connecting to the game server hosting the game ("
+ hostingHost + ") or starting own MasterBoard failed!\n"
+ "Reason: " + e.getMessage(), "Starting game failed!",
JOptionPane.ERROR_MESSAGE);
state = LoggedIn;
updateGUI();
}
catch (Exception e)
{
gc = null;
// client startup failed for some reason
JOptionPane.showMessageDialog(this,
"Unexpected exception while starting the spectator client: "
+ e.toString(), "Starting game failed!",
JOptionPane.ERROR_MESSAGE);
state = LoggedIn;
updateGUI();
}
}
public void gameCancelled(String gameId, String byUser)
{
deletedGames.add(gameId);
GameInfo gi = findGameByIdNoComplaint(gameId);
if (gi != null)
{
// Remove it from table
handleGameInfoUpdates(gi);
}
if (state == EnrolledInstantGame
&& enrolledInstantGameId.equals(gameId))
{
if (!byUser.equals(username))
{
String message = "Instant game " + gameId
+ " was cancelled by user " + byUser;
JOptionPane.showMessageDialog(this, message);
}
if (watchingInstantGameId != null)
{
// should never happen, in any case prevent it from setting state
// to plain "LoggedNn"
}
else
{
state = LoggedIn;
enrolledInstantGameId = null;
}
updateGUI();
}
}
public void chatDeliver(String chatId, long when, String sender,
String message, boolean resent)
{
if (chatId.equals(IWebServer.generalChatName))
{
generalChat.chatDeliver(when, sender, message, resent);
}
else
{
// chat delivery to chat other than general not implemented
}
}
public void notifyItsPlayersTurn(boolean shouldDoSomethingOrNot)
{
Color color = shouldDoSomethingOrNot ? Color.yellow : HTMLColor.white;
generalChat.setBackgroundColor(color);
}
public void watchGameInfo(String gameId, String host, int port)
{
LOGGER.info("Got watchgame info for game " + gameId + ": host=" + host
+ ", port=" + port);
startSpectatorClient(gameId, port, host);
}
public void tellOwnInfo(String email)
{
this.email = email;
}
public void requestAttention(long when, String byUser, boolean byAdmin,
String message, int beepCount, long beepInterval, boolean windows)
{
String whenText = "";
if (when != 0)
{
Calendar now = Calendar.getInstance(myLocale);
String nowDateString = myDateFormat.format(now.getTime());
String nowTimeString = myTimeFormat.format(now.getTime());
whenText = "At " + nowDateString + " " + nowTimeString;
}
String who = (byAdmin ? "Administrator" : "User") + " '" + byUser;
final String dialogTitle = who + "' requests your attention!";
final String dialogMessage = whenText + ", " + dialogTitle + "\n\n"
+ message;
final boolean toFront = options.getOption(Options.uponMessageToFront,
true);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
showRequestAttentionMessage(dialogTitle, dialogMessage,
toFront);
}
});
for (int i = 1; i <= beepCount; i++)
{
getToolkit().beep();
if (i < beepCount)
{
WhatNextManager.sleepFor(beepInterval);
}
}
}
/*
* Must be called inside EDT
*/
private void showRequestAttentionMessage(String dialogTitle,
String dialogMessage, boolean toFront)
{
if (toFront)
{
KFrame thisFrame = WebClient.this;
if (thisFrame.getState() != KFrame.NORMAL)
{
thisFrame.setState(KFrame.NORMAL);
}
thisFrame.toFront();
thisFrame.repaint();
}
JOptionPane.showMessageDialog(WebClient.this, dialogMessage,
dialogTitle, JOptionPane.INFORMATION_MESSAGE);
}
public void deliverGeneralMessage(long when, boolean error, String title,
String message)
{
String whenText = "";
if (when != 0)
{
Calendar now = Calendar.getInstance(myLocale);
String nowDateString = myDateFormat.format(now.getTime());
String nowTimeString = myTimeFormat.format(now.getTime());
whenText = "At " + nowDateString + " " + nowTimeString + ":\n";
}
JOptionPane.showMessageDialog(this, whenText + message, title,
error ? JOptionPane.ERROR_MESSAGE
: JOptionPane.INFORMATION_MESSAGE);
}
// TODO instead of to chat, add such stuff to a system log tab or similar
public void systemMessage(long when, String message)
{
generalChat.chatDeliver(when, "SYSTEM", message, false);
}
// Game Client tells us this when user closes the masterboard
public void tellGameEnds()
{
enrolledInstantGameId = null;
clearWatching();
updateGUI();
}
// Server tells us the amount of players in the different states
public void userInfo(int loggedin, int enrolled, int playing, int dead,
long ago, String text)
{
usersLoggedIn = loggedin;
usersEnrolled = enrolled;
usersPlaying = playing;
usersDead = dead;
usersLogoffAgo = ago;
usersText = text;
updateGUI();
}
/*
* Server tells us news about games in "proposed" state
* - created ones, enrolled player count changed, or
* when it is removed (cancelled or started)
*
**/
public void gameInfo(GameInfo gi)
{
if (deletedGames.contains(gi.getGameId()))
{
LOGGER.info("Still GameInfo update to gameId " + gi.getGameId()
+ " which is already deleted - ignoring it.");
return;
}
LOGGER.finest("In WC.gameInfo() for not deleted game with id " + gi);
handleGameInfoUpdates(gi);
}
private void handleGameInfoUpdates(GameInfo gi)
{
synchronized (gamesUpdates)
{
gamesUpdates.add(gi);
}
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
synchronized (gamesUpdates)
{
Iterator<GameInfo> it = gamesUpdates.iterator();
while (it.hasNext())
{
GameInfo game = it.next();
String gameId = game.getGameId();
if (deletedGames.contains(gameId))
{
proposedGameDataModel.removeGame(gameId);
break;
}
GameState gameState = game.getGameState();
switch (gameState)
{
case PROPOSED:
replaceInTable(proposedGameTable, game);
break;
case DUE:
case ACTIVATED:
replaceInTable(proposedGameTable, game);
break;
case STARTING:
// TODO: state never used anywhere else - remove it ?
break;
case READY_TO_CONNECT:
replaceInTable(proposedGameTable, game);
break;
case RUNNING:
replaceInTable(runGameTable, game);
proposedGameDataModel.removeGame(game
.getGameId());
suspGameDataModel.removeGame(game.getGameId());
break;
case SUSPENDED:
if (game.isEnrolled(username) || isAdmin())
{
replaceInTable(suspGameTable, game);
}
else
{
suspGameDataModel.removeGame(game
.getGameId());
}
runGameDataModel.removeGame(game.getGameId());
break;
case ENDING:
// Normally happens during change to RUNNING,
// but if starting fails, it's changed to
// ENDING immediately. So do it here again.
// If it is not there anymore, no harm done.
proposedGameDataModel.removeGame(game
.getGameId());
runGameDataModel.removeGame(game.getGameId());
break;
case DELETED:
proposedGameDataModel.removeGame(game
.getGameId());
runGameDataModel.removeGame(game.getGameId());
suspGameDataModel.removeGame(game.getGameId());
break;
default:
LOGGER.log(
Level.WARNING,
"Huups, unhandled game state "
+ game.getStateString());
}
}
gamesUpdates.clear();
}
updateGUI();
}
});
}
private void replaceInTable(JTable table, GameInfo gi)
{
GameTableModel model = (GameTableModel)table.getModel();
int index = model.getRowIndex(gi).intValue();
model.setRowAt(gi, index);
table.repaint();
}
public void connectionReset(boolean forced)
{
String message = (forced ? "Some other connection to server with same login name forced your logout."
: "Connection reset by server! Your WebClient was logged out.\n"
+ "(A possibly running game is _NOT_ affected by this!)");
JOptionPane.showMessageDialog(this, message);
setAdmin(false);
state = NotLoggedIn;
enrolledInstantGameId = null;
watchingInstantGameId = null;
receivedField.setText("Connection reset by server!");
updateStatus("Not connected", Color.red);
loginField.setEnabled(true);
synchronized (gamesUpdates)
{
gamesUpdates.clear();
}
gameHash.clear();
proposedGameDataModel.resetTable();
runGameDataModel.resetTable();
updateGUI();
tabbedPane.setSelectedComponent(serverTab);
}
// ======================================================================
// Below methods are called my GUI event listeners
private void hideButtonAction()
{
setVisible(false);
}
private void watchButtonAction()
{
String gameId = getSelectedGameIdFromRunTable();
LOGGER.info("Watch button pressed for gameId = " + gameId
+ " - requesting info from server");
watchButton.setEnabled(false);
server.watchGame(gameId, username);
}
private void resumeGameButtonAction()
{
String gameId = getSelectedGameIdFromSuspTable();
LOGGER.fine("Resume Button, game nr " + gameId);
suspGameTable.clearSelection();
resumeButton.setEnabled(false);
startButton.setEnabled(false);
startLocallyButton.setEnabled(false);
cancelButton.setEnabled(false);
unenrollButton.setEnabled(false);
// TODO better handle with changing state, but not today...
reasonWhyNotLabel.setText(startClickedText);
gameResumeInitiated = true;
updateGUI();
Thread.yield();
// Tell server
doResume(gameId);
}
private void deleteButtonAction()
{
String gameId = getSelectedGameIdFromSuspTable();
if (gameId == null)
{
LOGGER
.fine("Delete Suspended Game Button, but no game selected in table?");
return;
}
suspGameTable.clearSelection();
LOGGER.fine("Delete Suspended Game Button, game nr " + gameId);
doDeleteSuspendedGame(gameId);
}
private void quitButtonAction()
{
doQuit();
}
void loginLogoutButtonAction(final String command)
{
Runnable r = new Runnable()
{
public void run()
{
executeLoginLogoutButtonAction(command);
}
};
Thread doButtonAction = new Thread(r);
doButtonAction.start();
}
private void executeLoginLogoutButtonAction(String command)
{
if (command.equals(LoginButtonText))
{
doLogin();
}
else if (command.equals(LogoutButtonText))
{
doLogout();
}
else if (command.equals(CancelLoginButtonText))
{
doCancelConnect();
}
else
{
LOGGER.warning("unexpected command " + command
+ " on LoginButton?");
}
}
private void rereadLoginMsgButtonAction()
{
if (isAdmin())
{
server.rereadLoginMessage();
}
}
private void shutdownButtonAction()
{
if (isAdmin())
{
server.shutdownServer();
}
}
private void dumpInfoButtonAction()
{
server.dumpInfo();
}
private void debugSubmitButtonAction()
{
String text = commandField.getText();
((WebClientSocketThread)server).submitAnyText(text);
commandField.setText("");
}
private void registerOrPasswordButtonAction(String command)
{
// createAccountButtonText chgPasswordButtonText
if (command.equals(createAccountButtonText))
{
doRegisterOrPasswordDialog(true);
}
else if (command.equals(chgPasswordButtonText))
{
doRegisterOrPasswordDialog(false);
}
}
private void contactAdmin()
{
contactAdminButton.setText(contactAdminButtonDisabledText);
contactAdminButton.setEnabled(false);
new ContactAdminDialog(this, options, username, email);
}
public void reEnableContactAdminButton()
{
contactAdminButton.setText(contactAdminButtonText);
contactAdminButton.setEnabled(true);
}
void sendTheMessageToAdmin(String senderName, String senderEmail,
String message)
{
long now = new Date().getTime();
List<String> lines = Split.split("\n", message);
server.messageToAdmin(now, senderName, senderEmail, lines);
}
private void startLocallyButtonAction()
{
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId != null)
{
boolean ok = doStartLocally(selectedGameId);
if (ok)
{
// proposedGameTable.setEnabled(false);
}
}
}
private void startButtonAction()
{
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId != null)
{
boolean ok = doStart(selectedGameId);
if (ok)
{
// proposedGameTable.setEnabled(false);
}
}
}
private void cancelButtonAction()
{
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId != null)
{
GameInfo gi = findGameById(selectedGameId);
if (gi == null)
{
return;
}
if (gi.isEnrolled(username))
{
doUnenroll(selectedGameId);
}
doCancel(selectedGameId);
}
}
private void unenrollButtonAction()
{
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId != null)
{
boolean ok = doUnenroll(selectedGameId);
if (ok)
{
// proposedGameTable.setEnabled(true);
}
}
}
/**
* Find all "relevant" instant games owned by this player
* (relevant means except those who are running, ending or deleted).
* Normally there should ever be only one, but in strange cases...
* (like, game start failed or something...)
*/
private List<GameInfo> findMyInstantGames()
{
List<GameInfo> list = new ArrayList<GameInfo>();
for (GameInfo gi : gameHash.values())
{
// is instant, is mine, and don't bother about running, ending,
// suspended or deleted games, nor old ones.
if (gi.getInitiator().equals(username) && !gi.isScheduledGame()
&& !gi.getGameState().equals(GameState.RUNNING)
&& !gi.getGameState().equals(GameState.ENDING)
&& !gi.getGameState().equals(GameState.SUSPENDED)
&& !gi.getGameState().equals(GameState.DELETED)
&& !deletedGames.contains(gi.getGameId()))
{
list.add(gi);
}
}
return list;
}
/**
* If there is at least one instant game by this player,
* return it (one of it if many), otherwise null.
* Normally there should ever be only one, but in strange cases...
* (like, game start failed or something...)
* @return The (or: any) instant game or null
*/
private GameInfo ownInstantGameIfAny()
{
List<GameInfo> list = findMyInstantGames();
return list.isEmpty() ? null : list.get(0);
}
private void displayOnlyOneInstantGameMessage(String action, String message)
{
String title = "Can't " + action + " instant game now!";
JOptionPane.showMessageDialog(this, message, title,
JOptionPane.INFORMATION_MESSAGE);
}
private void enrollButtonAction()
{
String selectedGameId = getSelectedGameIdFromProposedTable();
if (selectedGameId != null)
{
GameInfo gi = findGameById(selectedGameId);
if (gi != null && !gi.isScheduledGame())
{
if (enrolledInstantGameId != null)
{
displayOnlyOneInstantGameMessage("enroll to",
"You can only be active for one instant game at a time, "
+ "and you are already enrolled to instant game #"
+ enrolledInstantGameId + "!");
return;
}
GameInfo instantGame = ownInstantGameIfAny();
if (instantGame != null
&& !instantGame.getGameId().equals(selectedGameId))
{
displayOnlyOneInstantGameMessage("enroll to",
"You can only be active for one instant game at a time, "
+ "and there is already instant game #"
+ instantGame.getGameId() + " proposed by you!");
return;
}
}
doEnroll(selectedGameId);
}
}
private void proposeButtonAction()
{
if (!getScheduledGamesMode())
{
if (enrolledInstantGameId != null)
{
displayOnlyOneInstantGameMessage("propose",
"You can only be active for one instant game at a time, "
+ "and you are already enrolled to instant game #"
+ enrolledInstantGameId + "!");
return;
}
GameInfo instantGame = null;
if ((instantGame = ownInstantGameIfAny()) != null)
{
displayOnlyOneInstantGameMessage(
"propose",
"You can only be active for one instant game at a time, "
+ "and there is already instant game #"
+ instantGame.getGameId() + " proposed by you!");
return;
}
if (gameClient != null)
{
displayOnlyOneInstantGameMessage("propose",
"You can only be connected to one game at a time, "
+ "but you are still connected to game #"
+ getOngoingGameId() + "." + "\n\n"
+ "Close the MasterBoard of that game first.");
return;
}
if (watchingInstantGameId != null)
{
displayOnlyOneInstantGameMessage("propose",
"You can only be connected to one game at a time, "
+ "but you are still connected to (watching) game #"
+ watchingInstantGameId + "." + "\n\n"
+ "Close the MasterBoard of that game first.");
return;
}
}
int min = ((Integer)spinner1.getValue()).intValue();
int target = ((Integer)spinner2.getValue()).intValue();
int max = ((Integer)spinner3.getValue()).intValue();
List<String> gameOptionsList = new ArrayList<String>();
for (String optionName : gameOptions)
{
if (checkboxForOption.get(optionName).isSelected())
{
gameOptionsList.add(optionName);
}
}
List<String> teleportOptionsList = new ArrayList<String>();
for (String optionName : teleportOptions)
{
if (checkboxForOption.get(optionName).isSelected())
{
teleportOptionsList.add(optionName);
}
}
boolean scheduled = getScheduledGamesMode();
long startAt = scheduled ? getStartTime() : -1;
int duration = getDuration();
String summaryText = getSummaryText();
do_proposeGame(variantBox.getSelectedItem().toString(), viewmodeBox
.getSelectedItem().toString(), startAt, duration, summaryText,
eventExpiringBox.getSelectedItem().toString(), gameOptionsList,
teleportOptionsList, min, target, max);
}
}