1
2
3
4
5 package spellcast.ui;
6
7 import java.awt.BorderLayout;
8 import java.awt.Color;
9 import java.awt.Dialog;
10 import java.awt.Dimension;
11 import java.awt.GridBagConstraints;
12 import java.awt.GridBagLayout;
13 import java.awt.Insets;
14 import java.awt.Toolkit;
15 import java.awt.event.ActionEvent;
16 import java.awt.event.ActionListener;
17 import java.awt.event.WindowAdapter;
18 import java.awt.event.WindowEvent;
19 import java.beans.PropertyChangeEvent;
20 import java.beans.PropertyChangeListener;
21 import java.util.Iterator;
22
23 import javax.swing.BorderFactory;
24 import javax.swing.JButton;
25 import javax.swing.JFrame;
26 import javax.swing.JMenu;
27 import javax.swing.JMenuBar;
28 import javax.swing.JMenuItem;
29 import javax.swing.JPanel;
30 import javax.swing.JScrollPane;
31 import javax.swing.JTextField;
32 import javax.swing.JTextPane;
33 import javax.swing.SwingUtilities;
34 import javax.swing.UIDefaults;
35 import javax.swing.UIManager;
36 import javax.swing.border.Border;
37 import javax.swing.border.EmptyBorder;
38 import javax.swing.event.SwingPropertyChangeSupport;
39 import javax.swing.text.BadLocationException;
40 import javax.swing.text.DefaultStyledDocument;
41
42 import org.apache.log4j.Logger;
43
44 import spellcast.beings.IWizard;
45 import spellcast.beings.Wizard;
46 import spellcast.event.OkEvent;
47 import spellcast.game.Game;
48 import spellcast.model.Id;
49 import spellcast.questions.Question;
50 import ui.PointUtilities;
51
52 /***
53 * This window contains the three main displays of Spellcast; the
54 * Text Display, Wizard Display and Query Display.
55 * <p>
56 * This class will fire the following property change events:
57 * <ul>
58 * <li>CONNECT_PROP
59 * <li>NEW_GAME_PROP
60 * <li>DISCONNECT_PROP
61 * <li>OK_PROP
62 * <li>MESSAGE_PROP
63 * </ul>
64 *
65 * @author Barrie Treloar
66 */
67 public class SpellcastWindow
68 extends JFrame
69 implements SpellcastView, UIProperties {
70
71 private static final Dimension TEXT_PREFERRED_SIZE = new Dimension(400, 312);
72
73 private JPanel panel;
74
75 private DefaultStyledDocument document;
76 private WizardDisplay wizardDisplay;
77 private QueryDisplay queryDisplay;
78
79 private JButton c_okButton;
80 private JTextField c_message;
81 private JMenuItem newGame;
82 private JMenuItem connect;
83 private JMenuItem disconnect;
84 private JMenuItem profiles;
85 private Dialog connectionDialog;
86 private Dialog newGameDialog;
87
88 private Id clientID;
89 private Game theGame;
90 private boolean disconnectAndExit;
91
92 private SwingPropertyChangeSupport propertySupport;
93
94 private static final Logger logger = Logger.getLogger("client.gui");
95
96
97 private final JFrame thisFrame;
98
99 /***
100 * Creates the main window and hooks the UI together.
101 */
102 public SpellcastWindow() {
103 super("Spellcast");
104 setDefaults();
105 propertySupport = new SwingPropertyChangeSupport(this);
106 thisFrame = this;
107 setIcon();
108 panel = new JPanel(new BorderLayout());
109 panel.setBorder(BorderFactory.createEmptyBorder(10, 7, 10, 7));
110 JScrollPane s =
111 new JScrollPane(
112 panel,
113 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
114 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
115 this.setContentPane(s);
116 createMenu();
117 createTextDisplay();
118 createWizardDisplay();
119 createQueryDisplay();
120 createDialogs();
121 createEventHandlers();
122 init();
123 pack();
124 this.setLocation(PointUtilities.centeredOnScreen(this));
125 }
126
127 /***
128 * Initialise all member variables to a known clean "initial" state.
129 */
130 public void init() {
131 if (document.getLength() > 0) {
132 try {
133 document.remove(0, document.getLength());
134 }
135 catch (BadLocationException e) {
136 logger.error("Unexpected exception.", e);
137 }
138 }
139 wizardDisplay.init();
140 queryDisplay.init();
141 setOk("No Game.");
142 setActive(false);
143 c_message.setText("");
144 clientID = Id.NO_ONE;
145 }
146
147 private void exit() {
148 if (disconnect.isEnabled()) {
149 disconnectAndExit = true;
150 propertySupport.firePropertyChange(DISCONNECT_PROP, null, null);
151 }
152 else {
153 reallyExit();
154 }
155 }
156
157 private void reallyExit() {
158 thisFrame.setVisible(false);
159 thisFrame.dispose();
160 System.exit(0);
161 }
162
163 /***
164 * Sets the ok button to display the desired text.
165 */
166 private void setOk(final String text) {
167 SwingUtilities.invokeLater(new Runnable() {
168 public void run() {
169 c_okButton.setText(text);
170 }
171 });
172 }
173
174 /***
175 * Sets the wizard into the specified active state.
176 */
177 private void setActive(final boolean enabled) {
178 SwingUtilities.invokeLater(new Runnable() {
179 public void run() {
180 c_okButton.setEnabled(enabled);
181 }
182 });
183 }
184
185 public Dimension getPreferredSize() {
186 Dimension preferredSize = Toolkit.getDefaultToolkit().getScreenSize();
187 preferredSize.height = preferredSize.height - 60;
188 preferredSize.width = preferredSize.width - 30;
189 return preferredSize;
190 }
191
192 public Dimension getMaximumSize() {
193 Dimension preferredSize = Toolkit.getDefaultToolkit().getScreenSize();
194 preferredSize.height = preferredSize.height - 60;
195 preferredSize.width = preferredSize.width - 30;
196 return preferredSize;
197 }
198
199 private void createMenu() {
200 JMenuBar menubar = new JMenuBar();
201
202 JMenu game = new JMenu("Game");
203 game.setMnemonic('g');
204 newGame = new JMenuItem("Start New Game");
205 newGame.setMnemonic('n');
206 newGame.addActionListener(new ActionListener() {
207 public void actionPerformed(ActionEvent e) {
208 }
209 });
210 game.add(newGame);
211 connect = new JMenuItem("Connect...");
212 connect.setMnemonic('c');
213 connect.addActionListener(new ActionListener() {
214 public void actionPerformed(ActionEvent e) {
215 init();
216 }
217 });
218 game.add(connect);
219 disconnect = new JMenuItem("Disconnect");
220 disconnect.setMnemonic('d');
221 disconnect.setEnabled(false);
222 disconnect.addActionListener(new ActionListener() {
223 public void actionPerformed(ActionEvent e) {
224 }
225 });
226 game.add(disconnect);
227 game.addSeparator();
228 profiles = new JMenuItem("Profiles...");
229 profiles.setMnemonic('p');
230 profiles.addActionListener(new ActionListener() {
231 public void actionPerformed(ActionEvent e) {
232 WizardProfiles wp = new WizardProfiles();
233 wp.setLocation(PointUtilities.centeredOnScreen(wp));
234 wp.setVisible(true);
235 }
236 });
237 game.add(profiles);
238 game.addSeparator();
239 JMenuItem exit = new JMenuItem("Exit");
240 exit.setMnemonic('x');
241 exit.addActionListener(new ActionListener() {
242 public void actionPerformed(ActionEvent e) {
243 exit();
244 }
245 });
246 game.add(exit);
247
248 menubar.add(game);
249
250 setJMenuBar(menubar);
251 }
252
253 private void createTextDisplay() {
254 document = new DefaultStyledDocument();
255 JTextPane textPane = new JTextPane(document);
256 textPane.setEditable(false);
257 JScrollPane s =
258 new JScrollPane(
259 textPane,
260 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
261 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
262 Border b =
263 BorderFactory.createCompoundBorder(
264 BorderFactory.createLineBorder(Color.black),
265 BorderFactory.createEmptyBorder(2, 2, 5, 5));
266 s.setViewportBorder(b);
267 s.setPreferredSize(TEXT_PREFERRED_SIZE);
268 panel.add(s, BorderLayout.WEST);
269 }
270
271 private void createWizardDisplay() {
272 wizardDisplay = new WizardDisplay();
273 panel.add(wizardDisplay, BorderLayout.CENTER);
274 }
275
276 private void createQueryDisplay() {
277 JPanel temp = new JPanel();
278 GridBagLayout gridbag = new GridBagLayout();
279 temp.setLayout(gridbag);
280 GridBagConstraints c = new GridBagConstraints();
281
282 c.insets = new Insets(4, 4, 4, 4);
283
284 c_okButton = new JButton();
285 gridbag.setConstraints(c_okButton, c);
286 temp.add(c_okButton);
287
288 c.gridwidth = GridBagConstraints.REMAINDER;
289 c.anchor = GridBagConstraints.WEST;
290 c.fill = GridBagConstraints.HORIZONTAL;
291 c.weightx = 1.0;
292 c_message = new JTextField();
293 c_message.addActionListener(new ActionListener() {
294 public void actionPerformed(ActionEvent e) {
295 String message = c_message.getText().trim();
296 c_message.setText("");
297 if (!message.equals("")) {
298 propertySupport.firePropertyChange(MESSAGE_PROP, null, message);
299 }
300 }
301 });
302 gridbag.setConstraints(c_message, c);
303 temp.add(c_message);
304
305 c.gridwidth = GridBagConstraints.REMAINDER;
306 c.anchor = GridBagConstraints.CENTER;
307 c.fill = GridBagConstraints.BOTH;
308 c.weightx = 0.0;
309 queryDisplay = new QueryDisplay();
310 gridbag.setConstraints(queryDisplay, c);
311 temp.add(queryDisplay);
312
313 panel.add(temp, BorderLayout.SOUTH);
314
315 }
316
317 private void createDialogs() {
318 connectionDialog = new ConnectionDialog();
319 newGameDialog = new NewGameDialog();
320 }
321
322 private void createEventHandlers() {
323 addWindowListener(new WindowAdapter() {
324 public void windowClosing(WindowEvent e) {
325 exit();
326 }
327 });
328 c_okButton.addActionListener(new ActionListener() {
329 public void actionPerformed(ActionEvent e) {
330 OkEvent event = new OkEvent();
331 event.setLeftHandGesture(wizardDisplay.getLeftHandGesture());
332 event.setRightHandGesture(wizardDisplay.getRightHandGesture());
333 event.setAnswers(queryDisplay.getAnswers());
334 propertySupport.firePropertyChange(OK_PROP, null, event);
335 }
336 });
337 }
338
339 public void addPropertyChangeListener(PropertyChangeListener listener) {
340 propertySupport.addPropertyChangeListener(listener);
341 }
342
343 public void removePropertyChangeListener(PropertyChangeListener listener) {
344 propertySupport.removePropertyChangeListener(listener);
345 }
346
347 private void setDefaults() {
348 UIDefaults defaults = UIManager.getDefaults();
349 defaults.put("GestureMenuItem.margin", new Insets(0, 0, 0, 0));
350 defaults.put("GestureMenuItem.borderPainted", Boolean.FALSE);
351 defaults.put("OptionPane.background", Color.white);
352 defaults.put("Panel.background", Color.white);
353 defaults.put("ScrollPane.background", Color.white);
354 defaults.put("ScrollBar.background", Color.white);
355 defaults.put("Table.focusCellHighlightBorder", new EmptyBorder(1, 2, 1, 2));
356 defaults.put("WizardDetails.readyColor", new Color(102, 102, 153));
357 defaults.put("WizardDetails.notReadyColor", new Color(200, 0, 20));
358
359 }
360
361 private void setIcon() {
362
363
364
365
366 }
367
368
369
370
371 /***
372 * Add a message to the view.
373 */
374 public void addMessage(String message) {
375 try {
376 document.insertString(document.getLength(), message, null);
377 }
378 catch (BadLocationException e) {
379 logger.error("Unexpected exception.", e);
380 }
381 }
382
383 /***
384 * Add a query to the view.
385 */
386 public void addQuery(Question query) {
387 queryDisplay.addQuery(query);
388 }
389
390 /***
391 * Add a wizard to the view.
392 */
393 public void addWizard(IWizard w) {
394
395 wizardDisplay.addWizard(w);
396 }
397
398 /***
399 * Remove a wizard from the view.
400 */
401 public void removeWizard(IWizard w) {
402
403 wizardDisplay.removeWizard(w);
404 }
405
406 /***
407 * Indicate that the system has connected to a Spellcast server.
408 * A successful connection will pass true, otherwise false is used.
409 * If unsuccessful then bring up the connection dialog again.
410 *
411 * @param success indicates whether the connection was success (true),
412 * or unsuccessful (false)
413 */
414 public void connected(boolean success) {
415 if (success) {
416 SwingUtilities.invokeLater(new Runnable() {
417 public void run() {
418 newGame.setEnabled(false);
419 connect.setEnabled(false);
420 disconnect.setEnabled(true);
421 profiles.setEnabled(false);
422 }
423 });
424 }
425 else {
426 connectionDialog.setVisible(true);
427 }
428 }
429
430 /***
431 * Indicate that the system has disconnected from a Spellcast server.
432 */
433 public void disconnected() {
434 SwingUtilities.invokeLater(new Runnable() {
435 public void run() {
436 newGame.setEnabled(true);
437 connect.setEnabled(true);
438 disconnect.setEnabled(false);
439 profiles.setEnabled(true);
440 if (disconnectAndExit) {
441 reallyExit();
442 }
443 }
444 });
445 }
446
447 /***
448 * Set the value of id.
449 *
450 * @param v Value to assign to id.
451 */
452 public void setID(Id id) {
453 clientID = id;
454 wizardDisplay.setID(id);
455 }
456
457 /***
458 * Set the value of the current Game.
459 * Go through all obejcts and add them to the view.
460 *
461 * @param v Value to assign to game.
462 */
463 public void setGame(Game v) {
464 theGame = v;
465 Iterator wizardIterator = v.getWizards().iterator();
466 while (wizardIterator.hasNext()) {
467 Wizard w = (Wizard) wizardIterator.next();
468 addWizard(w);
469 }
470 }
471
472 }