View Javadoc

1   /*
2   @See License.txt@
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      // Workaround for Modal Dialogs which lock up all threads.
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         // common constraints
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         //        ImageIcon windowIcon = new ImageIcon( GestureImages.getInstance().getDigitLefthandImage().getScaledInstance( 16, 16,
363         //           Image.SCALE_SMOOTH ) );
364         //        UIDefaults defaults = UIManager.getDefaults();
365         //        defaults.put( "InternalFrame.icon", new IconUIResource( windowIcon ) );
366     }
367 
368     /* ---------------------------------------------------------------------- */
369     /* Spellcast View Inteface                                                */
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 //        w.addPropertyChangeListener(this);
395         wizardDisplay.addWizard(w);
396     }
397 
398     /***
399      * Remove a wizard from the view.
400      */
401     public void removeWizard(IWizard w) {
402 //        w.removePropertyChangeListener(this);
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 }