/* * $Id$ * * Copyright (c) 2000-2009 by Rodney Kinney, Brent Easton * * 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.chat.ui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.jdesktop.swingworker.SwingWorker; import VASSAL.chat.Player; import VASSAL.chat.ServerStatus; import VASSAL.chat.SimpleRoom; import VASSAL.i18n.Resources; import VASSAL.tools.ErrorDialog; /** * Shows the current status of connections to the server */ public class ServerStatusView extends JTabbedPane implements ChangeListener, TreeSelectionListener { private static final long serialVersionUID = 1L; public static final String SELECTION_PROPERTY = "ServerStatusView.selection"; //$NON-NLS-1$ private ServerStatus status; private DefaultTreeModel model; private DefaultTreeModel[] historicalModels; private JTree treeCurrent; private JTree[] historicalTrees; private int totalPlayers; public ServerStatusView(ServerStatus status) { this.status = status; initComponents(); } private void initComponents() { JPanel current = new JPanel(new BorderLayout()); JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); JButton b = new JButton(Resources.getString("Chat.refresh")); //$NON-NLS-1$ b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { refresh(); } }); toolbar.add(b); current.add(toolbar, BorderLayout.NORTH); treeCurrent = createTree(); current.add(new JScrollPane(treeCurrent), BorderLayout.CENTER); model = (DefaultTreeModel) treeCurrent.getModel(); addTab(Resources.getString("Chat.current"), current); //$NON-NLS-1$ addChangeListener(this); setBorder(new TitledBorder(Resources.getString("Chat.server_connections"))); //$NON-NLS-1$ setStatusServer(status); } private void buildHistoricalTabs() { while (getTabCount() > 1) { removeTabAt(getTabCount()-1); } if (status != null) { String[] supported = status.getSupportedTimeRanges(); historicalTrees = new JTree[supported.length]; historicalModels = new DefaultTreeModel[supported.length]; for (int i = 0; i < supported.length; i++) { historicalTrees[i] = createTree(); historicalModels[i] = (DefaultTreeModel) historicalTrees[i].getModel(); addTab(supported[i], new JScrollPane(historicalTrees[i])); } } } private JTree createTree() { DefaultMutableTreeNode root = new DefaultMutableTreeNode(Resources.getString(Resources.VASSAL)); DefaultTreeModel m = new DefaultTreeModel(root, true); JTree tree = new JTree(m); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); tree.setCellRenderer(new Render()); tree.expandRow(0); tree.setLargeModel(true); tree.setRowHeight(18); // FIXME: check whether this is necessary tree.addTreeSelectionListener(this); tree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(TreeExpansionEvent event) { JComponent c = (JComponent) event.getSource(); c.setSize(c.getPreferredSize()); c.revalidate(); } public void treeCollapsed(TreeExpansionEvent event) { } }); return tree; } public void stateChanged(ChangeEvent e) { if (status == null) return; refresh(getSelectedIndex()); fireSelectionChanged(); } public void valueChanged(TreeSelectionEvent e) { fireSelectionChanged(); } private void fireSelectionChanged() { Object selection = null; TreePath path = null; int sel = getSelectedIndex(); switch (sel) { case 0: path = treeCurrent.getSelectionPath(); break; default: path = historicalTrees[sel-1].getSelectionPath(); break; } if (path != null) { selection = path.getLastPathComponent(); } if (selection instanceof DefaultMutableTreeNode) { selection = ((DefaultMutableTreeNode) selection).getUserObject(); } firePropertyChange(SELECTION_PROPERTY, null, selection); } public void refresh() { refresh(0); } private SwingWorker<ServerStatus.ModuleSummary[],Void> cur_request = null; private SwingWorker<ServerStatus.ModuleSummary[],Void> hist_request = null; private void refresh(final int page) { if (page == 0) { if (cur_request != null && !cur_request.isDone()) return; setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); cur_request = new SwingWorker<ServerStatus.ModuleSummary[],Void>() { @Override public ServerStatus.ModuleSummary[] doInBackground() { return status.getStatus(); } @Override protected void done() { try { if (getSelectedIndex() == 0) { refresh(model, get()); fireSelectionChanged(); } } catch (InterruptedException ex) { ErrorDialog.bug(ex); } // FIXME: review error message catch (ExecutionException ex) { ex.printStackTrace(); } if (hist_request == null || hist_request.isDone()) setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); cur_request = null; } }; cur_request.execute(); } else { if (hist_request != null && !hist_request.isDone()) return; setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); hist_request = new SwingWorker<ServerStatus.ModuleSummary[],Void>() { @Override public ServerStatus.ModuleSummary[] doInBackground() { return status.getHistory(getTitleAt(page)); } @Override protected void done() { final int sel = getSelectedIndex(); if (sel == page) { // page didn't change, refresh with what we computed try { refresh(historicalModels[sel-1], get()); } // FIXME: review error message catch (InterruptedException ex) { ex.printStackTrace(); } // FIXME: review error message catch (ExecutionException ex) { ex.printStackTrace(); } fireSelectionChanged(); } else if (sel != 0) { // page changed, refresh that page instead hist_request = null; refresh(sel); } if (cur_request == null || cur_request.isDone()) setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }; hist_request.execute(); } } private void refresh(DefaultTreeModel m, ServerStatus.ModuleSummary[] modules) { final MutableTreeNode root = (MutableTreeNode) m.getRoot(); totalPlayers = 0; while (root.getChildCount() > 0) { m.removeNodeFromParent((MutableTreeNode) root.getChildAt(0)); } if (modules.length == 0) { final DefaultMutableTreeNode n = new DefaultMutableTreeNode( Resources.getString("Chat.no_connections")); //$NON-NLS-1$ n.setAllowsChildren(false); } else { for (ServerStatus.ModuleSummary s : modules) { m.insertNodeInto(createNode(s), root, root.getChildCount()); } } // append total number of players on server to root node root.setUserObject( Resources.getString(Resources.VASSAL) + " (" + totalPlayers + ")"); } private DefaultMutableTreeNode createNode(Object o) { Object[] children = null; if (o instanceof ServerStatus.ModuleSummary) { final ServerStatus.ModuleSummary ms = (ServerStatus.ModuleSummary) o; children = ms.getRooms(); final int players = ms.numPlayers(); ms.setModuleName(ms.getModuleName() + " (" + players + ")"); totalPlayers += players; } else if (o instanceof SimpleRoom) { final SimpleRoom r = (SimpleRoom) o; final List<Player> l = r.getPlayerList(); // append the number of players to each room name r.setName(r.getName() + " (" + l.size() + ")"); children = l.toArray(new Player[l.size()]); } final DefaultMutableTreeNode node = new DefaultMutableTreeNode(o); if (children != null) { for (Object c : children) { node.add(createNode(c)); } } node.setAllowsChildren(children != null); return node; } public static class Render extends DefaultTreeCellRenderer { private static final long serialVersionUID = 1L; public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (leaf) { setIcon(null); } putClientProperty(Resources.getString("ServerStatusView.4"), Boolean.TRUE); //$NON-NLS-1$ return this; } } public void setStatusServer(ServerStatus status) { this.status = status; buildHistoricalTabs(); setEnabled(status != null); } }