/*
* SourceSelectionDialog.java
* Copyright 2010 Connor Petty <cpmeister@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on Apr 15, 2010, 7:15:13 PM
*/
package pcgen.gui2.sources;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import pcgen.facade.core.CampaignFacade;
import pcgen.facade.core.GameModeFacade;
import pcgen.facade.core.LoadableFacade.LoadingState;
import pcgen.facade.core.SourceSelectionFacade;
import pcgen.facade.util.ListFacade;
import pcgen.facade.util.ListFacades;
import pcgen.facade.util.SortedListFacade;
import pcgen.gui2.PCGenFrame;
import pcgen.gui2.UIPropertyContext;
import pcgen.gui2.dialog.DataInstaller;
import pcgen.gui2.filter.FilteredListFacadeTableModel;
import pcgen.gui2.tools.CommonMenuText;
import pcgen.gui2.tools.FlippingSplitPane;
import pcgen.gui2.tools.InfoPane;
import pcgen.gui2.tools.InfoPaneLinkAction;
import pcgen.gui2.tools.Utility;
import pcgen.gui2.util.FacadeListModel;
import pcgen.gui2.util.JTableEx;
import pcgen.gui2.util.table.TableUtils;
import pcgen.system.FacadeFactory;
import pcgen.system.LanguageBundle;
import pcgen.util.Comparators;
/**
*
* @author Connor Petty <cpmeister@users.sourceforge.net>
*/
public class SourceSelectionDialog extends JDialog
implements ActionListener, ChangeListener, ListSelectionListener
{
private static final UIPropertyContext context = UIPropertyContext.createContext("SourceSelectionDialog"); //$NON-NLS-1$
private static final String PROP_SELECTED_SOURCE = "selectedSource"; //$NON-NLS-1$
private static final String LOAD_COMMAND = "Load"; //$NON-NLS-1$
private static final String CANCEL_COMMAND = "Cancel"; //$NON-NLS-1$
private static final String SAVE_COMMAND = "Save"; //$NON-NLS-1$
private static final String DELETE_COMMAND = "Delete"; //$NON-NLS-1$
private static final String HIDEUNHIDE_COMMAND = "Hide"; //$NON-NLS-1$
private static final String INSTALLDATA_COMMAND = "Install"; //$NON-NLS-1$
private final PCGenFrame frame;
private QuickSourceSelectionPanel basicPanel;
private AdvancedSourceSelectionPanel advancedPanel;
private JTabbedPane tabs;
private JPanel buttonPanel;
private JButton loadButton;
private JButton cancelButton;
private JButton hideunhideButton;
private JButton deleteButton;
private JButton installDataButton;
private JButton saveButton;
private JCheckBox alwaysAdvancedCheck;
public SourceSelectionDialog(PCGenFrame frame)
{
super(frame, true);
this.frame = frame;
setTitle(LanguageBundle.getString("in_mnuSourcesLoadSelect")); //$NON-NLS-1$
this.tabs = new JTabbedPane();
this.basicPanel = new QuickSourceSelectionPanel();
this.advancedPanel = new AdvancedSourceSelectionPanel(frame);
this.buttonPanel = new JPanel();
this.loadButton = new JButton();
CommonMenuText.name(loadButton, "load"); //$NON-NLS-1$
this.cancelButton = new JButton();
CommonMenuText.name(cancelButton, "cancel"); //$NON-NLS-1$
this.hideunhideButton = new JButton();
CommonMenuText.name(hideunhideButton, "hideunhide"); //$NON-NLS-1$
this.deleteButton = new JButton();
CommonMenuText.name(deleteButton, "delete"); //$NON-NLS-1$
this.installDataButton = new JButton();
CommonMenuText.name(installDataButton, "mnuSourcesInstallData"); //$NON-NLS-1$
this.saveButton = new JButton();
CommonMenuText.name(saveButton, "saveSelection"); //$NON-NLS-1$
this.alwaysAdvancedCheck =
new JCheckBox(
LanguageBundle.getString("in_sourceAlwaysAdvanced"), //$NON-NLS-1$
!UIPropertyContext.getInstance().initBoolean(
UIPropertyContext.SOURCE_USE_BASIC_KEY, true));
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
initComponents();
initDefaults();
pack();
}
public static boolean skipSourceSelection()
{
return UIPropertyContext.getInstance().initBoolean(
UIPropertyContext.SKIP_SOURCE_SELECTION, false);
}
private void initComponents()
{
Container pane = getContentPane();
pane.setLayout(new BorderLayout());
tabs.add(LanguageBundle.getString("in_basic"), basicPanel); //$NON-NLS-1$
tabs.add(LanguageBundle.getString("in_advanced"), advancedPanel); //$NON-NLS-1$
tabs.addChangeListener(this);
pane.add(tabs, BorderLayout.CENTER);
loadButton.setDefaultCapable(true);
getRootPane().setDefaultButton(loadButton);
loadButton.setActionCommand(LOAD_COMMAND);
cancelButton.setActionCommand(CANCEL_COMMAND);
deleteButton.setActionCommand(DELETE_COMMAND);
saveButton.setActionCommand(SAVE_COMMAND);
hideunhideButton.setActionCommand(HIDEUNHIDE_COMMAND);
installDataButton.setActionCommand(INSTALLDATA_COMMAND);
loadButton.addActionListener(this);
cancelButton.addActionListener(this);
saveButton.addActionListener(this);
deleteButton.addActionListener(this);
hideunhideButton.addActionListener(this);
installDataButton.addActionListener(this);
Box buttons = Box.createHorizontalBox();
buttons.add(buttonPanel);
buttons.add(Box.createHorizontalGlue());
buttons.add(installDataButton);
buttons.add(Box.createHorizontalGlue());
buttons.add(loadButton);
buttons.add(Box.createHorizontalStrut(5));
buttons.add(cancelButton);
buttons.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
pane.add(buttons, BorderLayout.SOUTH);
Utility.installEscapeCloseOperation(this);
}
private void initDefaults()
{
boolean useBasic = context.initBoolean("useBasic", true); //$NON-NLS-1$
SourceSelectionFacade selection = basicPanel.getSourceSelection();
if (selection != null && useBasic)
{
basicPanel.makeSourceSelected(selection);
}
else
{
deleteButton.setEnabled(false);
}
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
if (useBasic)
{
setBasicButtons();
advancedPanel.setSourceSelection(basicPanel.getSourceSelection());
}
else
{
setAdvancedButtons();
tabs.setSelectedIndex(1);
}
}
void setDeleteEnabled(boolean enable)
{
deleteButton.setEnabled(enable);
}
void setLoadEnabled(boolean enable)
{
loadButton.setEnabled(enable);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
SourceSelectionFacade selection = basicPanel.getSourceSelection();
deleteButton.setEnabled(selection.isModifiable());
advancedPanel.setSourceSelection(selection);
}
}
private void setBasicButtons()
{
buttonPanel.removeAll();
buttonPanel.add(hideunhideButton);
buttonPanel.add(Box.createHorizontalStrut(5));
buttonPanel.add(deleteButton);
buttonPanel.revalidate();
}
private void setAdvancedButtons()
{
buttonPanel.removeAll();
buttonPanel.add(saveButton);
buttonPanel.add(alwaysAdvancedCheck);
buttonPanel.revalidate();
}
@Override
public void stateChanged(ChangeEvent e)
{
if (tabs.getSelectedComponent() == basicPanel)
{
setBasicButtons();
}
else
{
setAdvancedButtons();
}
}
@Override
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if (command.equals(SAVE_COMMAND))
{
final JList sourcesList = new JList();
final JTextField nameField = new JTextField();
ListFacade<SourceSelectionFacade> sources =
new SortedListFacade<>(
Comparators.toStringIgnoreCaseCollator(),
FacadeFactory.getCustomSourceSelections());
sourcesList.setModel(new FacadeListModel(sources));
sourcesList.addListSelectionListener(new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent lse)
{
nameField.setText(sourcesList.getSelectedValue().toString());
}
});
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JScrollPane(sourcesList), BorderLayout.CENTER);
panel.add(nameField, BorderLayout.SOUTH);
int ret = JOptionPane.showOptionDialog(
this,
panel, "Save the source selection as...",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, null, null);
if (ret == JOptionPane.OK_OPTION)
{
String name = nameField.getText();
List<CampaignFacade> selectedCampaigns = advancedPanel.getSelectedCampaigns();
GameModeFacade selectedGameMode = advancedPanel.getSelectedGameMode();
SourceSelectionFacade selection = null;
for (SourceSelectionFacade sourceSelectionFacade : sources)
{
if (sourceSelectionFacade.toString().equals(name))
{
selection = sourceSelectionFacade;
break;
}
}
if (selection == null)
{
selection = FacadeFactory.createCustomSourceSelection(name);
}
selection.setCampaigns(selectedCampaigns);
selection.setGameMode(selectedGameMode);
basicPanel.setSourceSelection(selection);
}
}
else if (command.equals(DELETE_COMMAND))
{
FacadeFactory.deleteCustomSourceSelection(basicPanel.getSourceSelection());
}
else if (command.equals(LOAD_COMMAND))
{
fireSourceLoad();
}
else if (command.equals(INSTALLDATA_COMMAND))
{
// Swap to the install data dialog.
setVisible(false);
DataInstaller di = new DataInstaller();
di.setVisible(true);
}
else if (command.equals(HIDEUNHIDE_COMMAND))
{
SourcesTableModel model = new SourcesTableModel();
JTableEx table = new JTableEx(model);
JTable rowTable = TableUtils.createDefaultTable();
JScrollPane pane = TableUtils.createCheckBoxSelectionPane(table, rowTable);
table.setShowGrid(false);
table.setFocusable(false);
table.setRowSelectionAllowed(false);
rowTable.setRowSelectionAllowed(false);
pane.setPreferredSize(new Dimension(300, 200));
int ret = JOptionPane.showOptionDialog(
this,
pane, "Select Sources to be visible",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, null, null);
if (ret == JOptionPane.OK_OPTION)
{
FacadeFactory.setDisplayedSources(model.getDisplayedSources());
}
model.dispose();
}
else
{//must be the cancel command
setVisible(false);
}
}
private void fireSourceLoad()
{
SourceSelectionFacade selection;
if (tabs.getSelectedComponent() == basicPanel)
{
selection = basicPanel.getSourceSelection();
}
else
{
selection = FacadeFactory.createSourceSelection(advancedPanel.getSelectedGameMode(), advancedPanel.getSelectedCampaigns());
}
if (selection == null)
{
return;
}
List<CampaignFacade> campaigns = ListFacades.wrap(selection.getCampaigns());
if (FacadeFactory.passesPrereqs(campaigns))
{
setVisible(false);
frame.loadSourceSelection(selection);
}
else
{
JOptionPane.showMessageDialog(this,
"Some sources have unfulfilled prereqs",
"Cannot Load Selected Sources",
JOptionPane.INFORMATION_MESSAGE);
}
}
@Override
public void setVisible(boolean visible)
{
if (visible)
{
advancedPanel.refreshDisplay();
}
else
{
UIPropertyContext.getInstance().setBoolean(
UIPropertyContext.SOURCE_USE_BASIC_KEY,
!alwaysAdvancedCheck.isSelected());
}
super.setVisible(visible);
}
private static class SourcesTableModel extends FilteredListFacadeTableModel<SourceSelectionFacade>
{
private List<SourceSelectionFacade> displayedSources;
public SourcesTableModel()
{
setDelegate(FacadeFactory.getSourceSelections());
displayedSources = new ArrayList<>();
displayedSources.addAll(ListFacades.wrap(FacadeFactory.getDisplayedSourceSelections()));
}
public void dispose()
{
//this detaches the listeners
setDelegate(null);
}
public SourceSelectionFacade[] getDisplayedSources()
{
return displayedSources.toArray(new SourceSelectionFacade[0]);
}
@Override
protected Object getValueAt(SourceSelectionFacade element, int column)
{
if (column == -1)
{
return displayedSources.contains(element);
}
else
{
return element;
}
}
@Override
public int getColumnCount()
{
return 1;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
if (columnIndex == -1)
{
return Boolean.class;
}
else
{
return Object.class;
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return columnIndex == -1;
}
@Override
public String getColumnName(int column)
{
return "Sources";
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
Boolean bool = (Boolean) aValue;
SourceSelectionFacade source = sortedList.getElementAt(rowIndex);
if (bool)
{
displayedSources.add(source);
}
else
{
displayedSources.remove(source);
}
}
}
private class QuickSourceSelectionPanel extends JPanel implements ListSelectionListener
{
private static final String DEFAULT_SOURCE = "Pathfinder RPG for Players"; //$NON-NLS-1$
private JList sourceList;
private InfoPane infoPane;
private InfoPaneLinkAction linkAction;
public QuickSourceSelectionPanel()
{
sourceList = new JList();
infoPane = new InfoPane(LanguageBundle.getString("in_src_info")); //$NON-NLS-1$
linkAction = new InfoPaneLinkAction(infoPane);
initComponents();
initDefaults();
}
private void initComponents()
{
setLayout(new BorderLayout());
JLabel label = new JLabel(LanguageBundle.getString("in_qsrc_intro"));
label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(label, BorderLayout.NORTH);
ListFacade<SourceSelectionFacade> sources =
new SortedListFacade<>(
Comparators.toStringIgnoreCaseCollator(),
FacadeFactory.getDisplayedSourceSelections());
sourceList.setModel(new FacadeListModel(sources));
sourceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
sourceList.setCellRenderer(new SourceListCellRenderer());
sourceList.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount() > 1)
{
Point p = e.getPoint();
int index = sourceList.locationToIndex(p);
if (sourceList.getCellBounds(index, index).contains(p))
{
fireSourceLoad();
}
}
}
});
FlippingSplitPane mainPane = new FlippingSplitPane(JSplitPane.HORIZONTAL_SPLIT, "quickSrcMain");
mainPane.setTopComponent(new JScrollPane(sourceList));
linkAction.install();
infoPane.setPreferredSize(new Dimension(800, 150));
mainPane.setBottomComponent(infoPane);
add(mainPane, BorderLayout.CENTER);
}
private void initDefaults()
{
final ListModel sortedModel = sourceList.getModel();
String defaultSelectedSource =
context.initProperty(PROP_SELECTED_SOURCE, DEFAULT_SOURCE);
int index = Collections.binarySearch(new AbstractList<Object>()
{
@Override
public Object get(int idx)
{
return sortedModel.getElementAt(idx);
}
@Override
public int size()
{
return sortedModel.getSize();
}
}, defaultSelectedSource, Comparators.toStringIgnoreCaseCollator());
if (index >= 0)
{
sourceList.setSelectedIndex(index);
}
else if (sortedModel.getSize() > 0)
{
sourceList.setSelectedIndex(0);
}
sourceList.addListSelectionListener(this);
}
public SourceSelectionFacade getSourceSelection()
{
return (SourceSelectionFacade) sourceList.getSelectedValue();
}
void setSourceSelection(SourceSelectionFacade source)
{
sourceList.setSelectedValue(source, true);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
SourceSelectionFacade selection = getSourceSelection();
if (selection != null)
{
context.setProperty(PROP_SELECTED_SOURCE, selection.toString());
makeSourceSelected(selection);
}
else
{
deleteButton.setEnabled(false);
}
}
/**
* Take the necessary action to show the source the currently selected
* source. This assumes that the source list has already been updated,
* either as a default setting, or from a user action. It does not set
* the source to be remembered as the last selection as that may
* interfere with startup defaults, so the caller must do that if
* responding to a user action.
* @param selection The sources selected.
*/
public void makeSourceSelected(SourceSelectionFacade selection)
{
advancedPanel.setSourceSelection(selection);
deleteButton.setEnabled(selection.isModifiable());
infoPane.setText(FacadeFactory.getCampaignInfoFactory()
.getHTMLInfo(selection));
}
private class SourceListCellRenderer extends DefaultListCellRenderer
{
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
setToolTipText("");
if (value instanceof SourceSelectionFacade)
{
SourceSelectionFacade selection = (SourceSelectionFacade) value;
if (selection.isModifiable())
{
setForeground(UIPropertyContext.getCustomItemColor());
}
if (selection.getLoadingState() == LoadingState.LOADED_WITH_ERRORS)
{
setForeground(Color.LIGHT_GRAY);
setToolTipText(selection.getLoadingErrorMessage());
}
}
return this;
}
}
}
}