/* * $Id$ * * Copyright (c) 2004 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.build.module.noteswindow; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DateFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import VASSAL.build.BadDataReport; import VASSAL.build.GameModule; import VASSAL.build.module.Chatter; import VASSAL.build.module.GameComponent; import VASSAL.build.module.GlobalOptions; import VASSAL.command.Command; import VASSAL.command.CommandEncoder; import VASSAL.configure.StringConfigurer; import VASSAL.configure.TextConfigurer; import VASSAL.i18n.Resources; import VASSAL.tools.ErrorDialog; import VASSAL.tools.ScrollPane; import VASSAL.tools.SequenceEncoder; import VASSAL.tools.WarningDialog; public class SecretNotesController implements GameComponent, CommandEncoder, AddSecretNoteCommand.Interface { public static final String COMMAND_PREFIX = "SNOTE\t"; //$NON-NLS-1$ private Controls controls; private JPanel panel; private List<SecretNote> notes; private List<SecretNote> lastSavedNotes; // Secret Note display table columns public static final int COL_HANDLE = 0; public static final int COL_DTM = 1; public static final int COL_NAME = 2; public static final int COL_REVEALED = 3; // Date formatter to save and restore date/times in the save file public static final DateFormat INTERNAL_DATE_FORMATTER = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US); // Date formatter to display date/time to the player public static final DateFormat LOCAL_DATE_FORMATTER = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()); public SecretNotesController() { notes = new ArrayList<SecretNote>(); controls = new Controls(); } public Command getRestoreCommand() { Command comm = null; for (SecretNote note : notes) { Command c = new AddSecretNoteCommand(this, note); if (comm == null) { comm = c; } else { comm.append(c); } } return comm; } public void setup(boolean gameStarting) { if (!gameStarting) { notes.clear(); controls.refresh(); } } public Command decode(String command) { Command comm = null; if (command.startsWith(COMMAND_PREFIX)) { final SequenceEncoder.Decoder st = new SequenceEncoder.Decoder (command.substring(COMMAND_PREFIX.length()), '\t'); String name = st.nextToken(); String owner = st.nextToken(); boolean hidden = "true".equals(st.nextToken()); //$NON-NLS-1$ String text = TextConfigurer.restoreNewlines(st.nextToken()); String handle = ""; //$NON-NLS-1$ Date date = null; if (st.hasMoreTokens()) { String formattedDate = st.nextToken(); try { date = INTERNAL_DATE_FORMATTER.parse(formattedDate); } catch (ParseException e) { ErrorDialog.dataError(new BadDataReport("Illegal date format",formattedDate,e)); } } if (st.hasMoreTokens()) { handle = st.nextToken(); } SecretNote note = new SecretNote(name, owner, text, hidden, date, handle); comm = new AddSecretNoteCommand(this, note); } return comm; } public String encode(Command c) { String s = null; if (c instanceof AddSecretNoteCommand) { SecretNote note = ((AddSecretNoteCommand) c).getNote(); SequenceEncoder se = new SequenceEncoder('\t'); String date = note.getDate() == null ? "" : INTERNAL_DATE_FORMATTER.format(note.getDate()); //$NON-NLS-1$ s = COMMAND_PREFIX + se.append(note.getName()) .append(note.getOwner()) .append(note.isHidden()) .append(TextConfigurer.escapeNewlines(note.getText())) .append(date) .append(note.getHandle()).getValue(); } return s; } public void addSecretNote(SecretNote note) { int index = notes.indexOf(note); if (index >= 0) { notes.set(index, note); } else { notes.add(0, note); } controls.refresh(); } public JComponent getControls() { if (panel == null) { panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); JLabel l = new JLabel(Resources.getString("Notes.visible_once_revealed")); //$NON-NLS-1$ l.setAlignmentX(0.0F); panel.add(l); panel.add(controls); } return panel; } public Command save() { Command comm = null; for (SecretNote secretNote : notes) { int index = lastSavedNotes.indexOf(secretNote); if (index < 0 || lastSavedNotes.get(index).isHidden() != secretNote.isHidden()) { Command c = new AddSecretNoteCommand(this, secretNote); if (comm == null) { comm = c; } else { comm.append(c); } String msg; if (index < 0) { msg = "* " + Resources.getString("Notes.has_created", GlobalOptions.getInstance().getPlayerId(), secretNote.getName()) + " *"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else { msg = "* " + Resources.getString("Notes.has_revealed", GlobalOptions.getInstance().getPlayerId(), secretNote.getName()) + " *"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } c = new Chatter.DisplayText(GameModule.getGameModule().getChatter(), msg); c.execute(); comm.append(c); } } return comm; } public void captureState() { lastSavedNotes = new ArrayList<SecretNote>(notes); } public void restoreState() { notes.clear(); notes.addAll(lastSavedNotes); controls.refresh(); } private class Controls extends JPanel implements ItemListener { private static final long serialVersionUID = 1L; private JTextArea text; private JTable table; private JButton revealButton; private String[] columnNames = { Resources.getString("Notes.player"), //$NON-NLS-1$ Resources.getString("Notes.date_time"), //$NON-NLS-1$ Resources.getString("Notes.note_name"), //$NON-NLS-1$ Resources.getString("Notes.revealed") //$NON-NLS-1$ }; public Controls() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); table = new JTable(new MyTableModel()); initColumns(table); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ListSelectionModel rowSM = table.getSelectionModel(); rowSM.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { //Ignore extra messages. if (e.getValueIsAdjusting()) return; ListSelectionModel lsm = (ListSelectionModel) e.getSource(); if (lsm.isSelectionEmpty()) { //...//no rows are selected } else { displaySelected(); } } }); JScrollPane secretScroll = new ScrollPane(table); table.setPreferredScrollableViewportSize(new Dimension(500, 100)); add(secretScroll); Box b = Box.createHorizontalBox(); b.setAlignmentX(0.0F); JButton newButton = new JButton(Resources.getString(Resources.NEW)); newButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { createNewNote(); } }); b.add(newButton); revealButton = new JButton(Resources.getString("Notes.reveal")); //$NON-NLS-1$ revealButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { revealSelectedNote(); } }); revealButton.setEnabled(false); b.add(revealButton); add(b); text = new JTextArea(6, 20); text.setEditable(false); JScrollPane scroll = new ScrollPane(text); scroll.setBorder(new TitledBorder(Resources.getString("Notes.text"))); //$NON-NLS-1$ add(scroll); } private void initColumns(JTable t) { TableColumn column = null; for (int i = 0; i < columnNames.length; i++) { column = t.getColumnModel().getColumn(i); int width; switch (i) { case COL_HANDLE: width = 60; break; case COL_DTM: width = 100; break; case COL_NAME: width = 280; break; case COL_REVEALED: width = 60; break; default: width = 100; break; } column.setPreferredWidth(width); } } public class MyTableModel extends AbstractTableModel { private static final long serialVersionUID = 1L; public String getColumnName(int col) { return columnNames[col]; } public int getRowCount() { return notes.size(); } public int getColumnCount() { return columnNames.length; } public Object getValueAt(int row, int col) { SecretNote note = notes.get(row); switch (col) { case COL_HANDLE: return note.getHandle(); case COL_DTM: return note.getDate() == null ? "" : LOCAL_DATE_FORMATTER.format(note.getDate()); //$NON-NLS-1$ case COL_NAME: return note.getName(); case COL_REVEALED: return Boolean.valueOf(!note.isHidden()); default: return null; } } public Class<?> getColumnClass(int c) { return getValueAt(0, c).getClass(); } public boolean isCellEditable(int row, int col) { return false; } public void setValueAt(Object value, int row, int col) { } } public void refresh() { table.setModel(new MyTableModel()); initColumns(table); displaySelected(); } private void revealSelectedNote() { int selectedRow = table.getSelectedRow(); if (selectedRow < 0) { return; } String selectedName = (String) table.getValueAt(selectedRow, COL_NAME); SecretNote note = getNoteForName(selectedName); if (note.getOwner().equals(GameModule.getUserId())) { note = new SecretNote(note.getName(), note.getOwner(), note.getText(), false, note.getDate(), note.getHandle()); if (note != null) { int i = notes.indexOf(note); notes.set(i, note); refresh(); } } } public void createNewNote() { final Dialog parent = (Dialog) SwingUtilities.getAncestorOfClass(Dialog.class, this); final JDialog d; if (parent != null) { d = new JDialog(parent, true); } else { d = new JDialog( (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), true ); } d.setTitle(Resources.getString("Notes.delayed_note")); //$NON-NLS-1$ final StringConfigurer name = new StringConfigurer(null, Resources.getString("Notes.name")); //$NON-NLS-1$ final TextConfigurer text = new TextConfigurer(null, Resources.getString("Notes.text")); //$NON-NLS-1$ d.setLayout(new BoxLayout(d.getContentPane(), BoxLayout.Y_AXIS)); d.add(name.getControls()); d.add(text.getControls()); final Box buttonPanel = Box.createHorizontalBox(); final JButton okButton = new JButton(Resources.getString(Resources.OK)); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { final SecretNote note = new SecretNote( name.getValueString(), GameModule.getUserId(), (String) text.getValue(), true ); if (notes.contains(note)) { WarningDialog.show(Controls.this, "Notes.note_exists"); } else { notes.add(0, note); refresh(); d.dispose(); } } }); final PropertyChangeListener l = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { okButton.setEnabled(name.getValueString() != null && name.getValueString().length() > 0 && text.getValueString() != null && text.getValueString().length() > 0); } }; name.addPropertyChangeListener(l); text.addPropertyChangeListener(l); okButton.setEnabled(false); buttonPanel.add(okButton); final JButton cancelButton = new JButton(Resources.getString(Resources.CANCEL)); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.dispose(); } }); d.add(buttonPanel); d.pack(); d.setLocationRelativeTo(d.getOwner()); d.setVisible(true); } public void itemStateChanged(ItemEvent e) { displaySelected(); } private void displaySelected() { revealButton.setEnabled(false); text.setText(""); //$NON-NLS-1$ int selectedRow = table.getSelectedRow(); if (selectedRow < 0) { return; } String selectedName = (String) table.getValueAt(selectedRow, COL_NAME); SecretNote note = getNoteForName(selectedName); if (note != null) { if (note.getOwner().equals(GameModule.getUserId())) { text.setText(note.getText()); revealButton.setEnabled(note.isHidden()); } else { text.setText(note.isHidden() ? Resources.getString("Notes.message_not_revealed") : note.getText()); //$NON-NLS-1$ } } } } public SecretNote getNoteForName(String s) { for (SecretNote n : notes) { if (n.getName().equals(s)) { return n; } } return null; } }