1
2
3
4
5 package spellcast.ui;
6
7 import java.awt.BorderLayout;
8 import java.awt.GridBagConstraints;
9 import java.awt.GridBagLayout;
10 import java.awt.Insets;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.beans.PropertyChangeListener;
14 import java.io.BufferedReader;
15 import java.io.BufferedWriter;
16 import java.io.File;
17 import java.io.FileReader;
18 import java.io.FileWriter;
19 import java.net.InetAddress;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.Iterator;
24
25 import javax.swing.BorderFactory;
26 import javax.swing.ComboBoxModel;
27 import javax.swing.DefaultComboBoxModel;
28 import javax.swing.JButton;
29 import javax.swing.JComboBox;
30 import javax.swing.JDialog;
31 import javax.swing.JFrame;
32 import javax.swing.JLabel;
33 import javax.swing.JPanel;
34 import javax.swing.JTextField;
35 import javax.swing.event.SwingPropertyChangeSupport;
36 import org.apache.log4j.Logger;
37 import spellcast.net.NetConstants;
38 import spellcast.util.TextUtil;
39
40 /***
41 * The New Game Dialog allows the user to create a new spellcast server.
42 * <p>
43 * A JComboBox is displayed with the label "Game Name" into which a game name
44 * can be entered. By default the values will be the read from the file
45 * <b>user.home</b>/.spellcast/GameNames.txt. The format of this file is described at the
46 * end of this document.
47 * <p>
48 * A JText field is displayed with the label "Server Address" into which a manual address
49 * can be entered. By default the value of <code>InetAddress.getLocalHost</code>, a colon, and
50 * <code>NetConstants.SPELLCAST_NET_DEFAULT_PORT</code> will be displayed.
51 * When a new value is filled in the port is optional and will default to the
52 * <code>NetConstants.SPELLCAST_NET_DEFAULT_PORT</code>
53 * <p>
54 * In the South area are two buttons for "Create" and "Cancel".
55 * <p>
56 * Format of <b>user.home</b>/.spellcast/GameNames.txt:<br>
57 * The purpose of this file is to retain the 10 most popular
58 * choice of game names for this user.<br>
59 * The file consists of at most 10 lines.<br>
60 * Each line is of the form: <number of uses> <game name>
61 * The number of uses indicates how many times this game name has been used.<br>
62 * The game name can contain any character and is limited to be 32 characters.<br>
63 * The file is written so that each line is sorted into most used to least used<br>
64 * <p>
65 * This class will fire NEW_GAME_PROP property change events.
66 * @author Barrie Treloar
67 */
68 public class NewGameDialog extends JDialog implements UIProperties {
69 private static final int MAX_GAME_NAMES = 10;
70 private static final int MAX_GAME_NAME_STRING_LENGTH = 32;
71
72 private JButton c_create;
73 private JButton c_cancel;
74 private JComboBox c_gameName;
75 private JTextField c_serverAddress;
76 private ArrayList mostRecentUsedGameNames = new ArrayList(MAX_GAME_NAMES);
77 private File mostRecentUsedGameNamesFile =
78 new File(System.getProperty("user.home") + "/.spellcast/GameNames.txt");
79
80 private SwingPropertyChangeSupport propertySupport;
81
82 private static final Logger logger = Logger.getLogger("client.connect");
83
84 public NewGameDialog() {
85 super((JFrame) null, "Create Spellcast Game", false);
86
87 propertySupport = new SwingPropertyChangeSupport(this);
88
89 createSouthPanel();
90 createCenterPanel();
91 pack();
92 }
93
94 public void addPropertyChangeListener(PropertyChangeListener listener) {
95 propertySupport.addPropertyChangeListener(listener);
96 }
97
98 public void removePropertyChangeListener(PropertyChangeListener listener) {
99 propertySupport.removePropertyChangeListener(listener);
100 }
101
102 private ComboBoxModel loadMostRecentUsedGameNames() {
103 DefaultComboBoxModel result = new DefaultComboBoxModel();
104 BufferedReader r = null;
105
106 try {
107 if (mostRecentUsedGameNamesFile.exists()) {
108 r = new BufferedReader(new FileReader(mostRecentUsedGameNamesFile));
109 String line = null;
110
111 for (int i = 0; i < 10 && (line = r.readLine()) != null; i++) {
112
113 int numberOfUses = TextUtil.getNumberOfUses(line);
114 String gameName = TextUtil.getGameName(line);
115 if (gameName.length() > MAX_GAME_NAME_STRING_LENGTH) {
116 gameName = gameName.substring(0, MAX_GAME_NAME_STRING_LENGTH);
117 logger.warn(
118 "Truncating game name that exceeds max length of "
119 + MAX_GAME_NAME_STRING_LENGTH);
120 }
121 if (numberOfUses == -1 || gameName == null) {
122 logger.warn(
123 "Malformed line (" + i + ") expecting format of " + "<number><space><string>");
124 }
125 else {
126 mostRecentUsedGameNames.add(numberOfUses + " " + gameName);
127 }
128 }
129 Collections.sort(mostRecentUsedGameNames, new GameNameComparator());
130
131 Iterator i = mostRecentUsedGameNames.iterator();
132 while (i.hasNext()) {
133 line = (String) i.next();
134 result.addElement(TextUtil.getGameName(line));
135 }
136 }
137 }
138 catch (Exception e) {
139 logger.error("Could not load Most Recent Used Game Names.", e);
140 }
141 finally {
142 try {
143 if (r != null) {
144 r.close();
145 }
146 }
147 catch (Exception e) {
148 logger.error("Unexpected error in Reader.close().", e);
149 }
150 }
151
152 return result;
153 }
154
155 private void saveMostRecentUsedGameNames(ComboBoxModel model) {
156 BufferedWriter w = null;
157
158 try {
159
160 mostRecentUsedGameNamesFile.createNewFile();
161
162 if (mostRecentUsedGameNamesFile.exists()) {
163
164
165 boolean gameNotInList = true;
166 String currentGameName = (String) model.getSelectedItem();
167 Iterator i = mostRecentUsedGameNames.iterator();
168 while (i.hasNext()) {
169 String line = (String) i.next();
170 String gameName = TextUtil.getGameName(line);
171 if (gameName.equals(currentGameName)) {
172 int numberOfUses = TextUtil.getNumberOfUses(line) + 1;
173 i.remove();
174 mostRecentUsedGameNames.add(numberOfUses + " " + gameName);
175 gameNotInList = false;
176 break;
177 }
178 }
179 if (gameNotInList) {
180 mostRecentUsedGameNames.add("1 " + currentGameName);
181 }
182 Collections.sort(mostRecentUsedGameNames, new GameNameComparator());
183
184 while (mostRecentUsedGameNames.size() > MAX_GAME_NAMES) {
185 mostRecentUsedGameNames.remove(mostRecentUsedGameNames.size() - 1);
186 }
187
188 w = new BufferedWriter(new FileWriter(mostRecentUsedGameNamesFile));
189 i = mostRecentUsedGameNames.iterator();
190 while (i.hasNext()) {
191 String line = (String) i.next();
192 w.write(line);
193 w.newLine();
194 }
195 }
196 }
197 catch (Exception e) {
198 logger.error("Could not save Most Recent Used Game Names.", e);
199 }
200 finally {
201 try {
202 if (w != null) {
203 w.flush();
204 w.close();
205 }
206 }
207 catch (Exception e) {
208 logger.error("Unexpected error in Writer.close().", e);
209 }
210 }
211
212 }
213
214 private void createSouthPanel() {
215 JPanel p = new JPanel();
216 GridBagLayout gridbag = new GridBagLayout();
217 p.setLayout(gridbag);
218 GridBagConstraints c = new GridBagConstraints();
219
220 c.insets = new Insets(4, 4, 4, 4);
221 c.fill = GridBagConstraints.HORIZONTAL;
222
223 c.weightx = 1.0;
224 JLabel empty = new JLabel(" ");
225 gridbag.setConstraints(empty, c);
226 p.add(empty);
227
228 c.weightx = 0.0;
229 c_create = new JButton("Create");
230 c_create.setDefaultCapable(true);
231 getRootPane().setDefaultButton(c_create);
232 c_create.addActionListener(new ActionListener() {
233 public void actionPerformed(ActionEvent e) {
234 String theGameName = (String) c_gameName.getSelectedItem();
235 if (theGameName == null || theGameName.equals("")) {
236 theGameName = "Spellcast";
237 c_gameName.addItem(theGameName);
238 c_gameName.setSelectedItem(theGameName);
239 }
240 String serverAddressString = c_serverAddress.getText().trim();
241 setVisible(false);
242
243 NewGameEvent evt = new NewGameEvent(theGameName, serverAddressString);
244 propertySupport.firePropertyChange(NEW_GAME_PROP, null, evt);
245 }
246 });
247 gridbag.setConstraints(c_create, c);
248 p.add(c_create);
249
250 c_cancel = new JButton("Cancel");
251 c_cancel.addActionListener(new ActionListener() {
252 public void actionPerformed(ActionEvent e) {
253 setVisible(false);
254 }
255 });
256 gridbag.setConstraints(c_cancel, c);
257 p.add(c_cancel);
258
259 getContentPane().add(p, BorderLayout.SOUTH);
260 }
261
262 private void createCenterPanel() {
263 JPanel p = new JPanel();
264 p.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
265
266 GridBagLayout gridbag = new GridBagLayout();
267 p.setLayout(gridbag);
268 GridBagConstraints c = new GridBagConstraints();
269
270 c.insets = new Insets(4, 4, 4, 4);
271
272 JLabel gameNameLabel = new JLabel("Game Name");
273 gridbag.setConstraints(gameNameLabel, c);
274 p.add(gameNameLabel);
275
276 c.gridwidth = GridBagConstraints.REMAINDER;
277 c.anchor = GridBagConstraints.WEST;
278 c_gameName = new JComboBox(loadMostRecentUsedGameNames());
279 c_gameName.setEditable(true);
280 gridbag.setConstraints(c_gameName, c);
281 p.add(c_gameName);
282 gameNameLabel.setLabelFor(c_gameName);
283
284 c.gridwidth = GridBagConstraints.RELATIVE;
285 c.anchor = GridBagConstraints.CENTER;
286 JLabel serverAddressLabel = new JLabel("Server Address");
287 gridbag.setConstraints(serverAddressLabel, c);
288 p.add(serverAddressLabel);
289
290 c.gridwidth = GridBagConstraints.REMAINDER;
291 c.anchor = GridBagConstraints.WEST;
292 c_serverAddress = new JTextField(12);
293 try {
294 c_serverAddress.setText(
295 InetAddress.getLocalHost().getHostAddress()
296 + ":"
297 + NetConstants.SPELLCAST_NET_DEFAULT_PORT);
298 }
299 catch (Exception e) {
300 logger.error("Could not get Localhost for setting Server Address.", e);
301 }
302 gridbag.setConstraints(c_serverAddress, c);
303 p.add(c_serverAddress);
304 serverAddressLabel.setLabelFor(c_serverAddress);
305
306 getContentPane().add(p, BorderLayout.CENTER);
307 }
308
309 /***
310 * This comparator imposes the correct order of GameNames based upon
311 * how many uses the Game Name has. The order is most uses first to least uses last.
312 * It Compares its two arguments for order.
313 * Returns a negative integer, zero, or a positive integer
314 * as the first argument is less than, equal to, or greater than the second.
315 * Note: this comparator imposes orderings that are inconsistent with equals.
316 */
317 class GameNameComparator implements Comparator {
318 public int compare(Object o1, Object o2) {
319 int result = 0;
320
321 String s1 = (String) o1;
322 String s2 = (String) o2;
323
324 int uses1 = TextUtil.getNumberOfUses(s1);
325 int uses2 = TextUtil.getNumberOfUses(s2);
326
327
328 if (uses1 > uses2) {
329 result = -1;
330 }
331 else
332 if (uses1 < uses2) {
333 result = 1;
334 }
335 else {
336 result = 0;
337 }
338
339 return result;
340 }
341 }
342
343 }