/* * DomainInfoTab.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 Aug 8, 2010, 4:29:55 PM */ package pcgen.gui2.tabs; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.DeityFacade; import pcgen.facade.core.DomainFacade; import pcgen.facade.core.InfoFacade; import pcgen.facade.core.InfoFactory; import pcgen.facade.util.DefaultListFacade; import pcgen.facade.util.ListFacade; import pcgen.facade.util.ReferenceFacade; import pcgen.facade.util.event.ListEvent; import pcgen.facade.util.event.ListListener; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.event.ReferenceListener; import pcgen.gui2.UIPropertyContext; import pcgen.gui2.filter.DisplayableFilter; import pcgen.gui2.filter.Filter; import pcgen.gui2.filter.FilterBar; import pcgen.gui2.filter.FilterButton; import pcgen.gui2.filter.FilterHandler; import pcgen.gui2.filter.FilteredListFacadeTableModel; import pcgen.gui2.filter.FilteredTreeViewTable; import pcgen.gui2.filter.SearchFilterPanel; import pcgen.gui2.tabs.models.CharacterTreeCellRenderer.Handler; import pcgen.gui2.tabs.models.QualifiedTreeCellRenderer; import pcgen.gui2.tools.FlippingSplitPane; import pcgen.gui2.tools.InfoPane; import pcgen.gui2.tools.PrefTableColumnModel; import pcgen.gui2.util.FontManipulation; import pcgen.gui2.util.JDynamicTable; import pcgen.gui2.util.table.DynamicTableColumnModel; import pcgen.gui2.util.table.TableUtils; import pcgen.gui2.util.treeview.DataView; import pcgen.gui2.util.treeview.DataViewColumn; import pcgen.gui2.util.treeview.DefaultDataViewColumn; import pcgen.gui2.util.treeview.TreeView; import pcgen.gui2.util.treeview.TreeViewModel; import pcgen.gui2.util.treeview.TreeViewPath; import pcgen.system.LanguageBundle; import pcgen.util.enumeration.Tab; /** * This component handles diety and domain selection for a character. * * @author Connor Petty <cpmeister@users.sourceforge.net> */ @SuppressWarnings("serial") public class DomainInfoTab extends FlippingSplitPane implements CharacterInfoTab, TodoHandler { private final FilteredTreeViewTable<Object, DeityFacade> deityTable; private final JDynamicTable domainTable; private final JTable domainRowHeaderTable; private final JLabel selectedDeity; private final JButton selectDeity; private final JLabel selectedDomain; private final InfoPane deityInfo; private final InfoPane domainInfo; private DisplayableFilter<CharacterFacade, DomainFacade> domainFilter; private static final Object COLUMN_ID = new Object(); private final FilterButton<Object, DeityFacade> qDeityButton; private final FilterButton<Object, DomainFacade> qDomainButton; private final QualifiedTreeCellRenderer qualifiedRenderer; public DomainInfoTab() { super("Domain"); this.deityTable = new FilteredTreeViewTable<>(); this.domainTable = new JDynamicTable(); this.domainRowHeaderTable = TableUtils.createDefaultTable(); this.selectedDeity = new JLabel(); this.selectDeity = new JButton(); this.selectedDomain = new JLabel(); this.deityInfo = new InfoPane("in_deityInfo"); //$NON-NLS-1$ this.domainInfo = new InfoPane("in_domainInfo"); //$NON-NLS-1$ this.qDeityButton = new FilterButton<>("DeityQualified"); this.qDomainButton = new FilterButton<>("DomainQualified"); this.qualifiedRenderer = new QualifiedTreeCellRenderer(); initComponents(); } private void initComponents() { setOrientation(VERTICAL_SPLIT); deityTable.setTreeCellRenderer(qualifiedRenderer); JPanel panel = new JPanel(new BorderLayout()); FilterBar<Object, DeityFacade> bar = new FilterBar<>(); bar.addDisplayableFilter(new SearchFilterPanel()); qDeityButton.setText(LanguageBundle.getString("in_igQualFilter")); //$NON-NLS-1$ bar.addDisplayableFilter(qDeityButton); deityTable.setDisplayableFilter(bar); panel.add(bar, BorderLayout.NORTH); ListSelectionModel selectionModel = deityTable.getSelectionModel(); selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); panel.add(new JScrollPane(deityTable), BorderLayout.CENTER); Box box = Box.createHorizontalBox(); box.add(Box.createHorizontalGlue()); box.add(new JLabel(LanguageBundle.getString("in_domDeityLabel"))); //$NON-NLS-1$ box.add(Box.createHorizontalStrut(5)); box.add(selectedDeity); box.add(Box.createHorizontalStrut(5)); box.add(selectDeity); box.add(Box.createHorizontalGlue()); panel.add(box, BorderLayout.SOUTH); FlippingSplitPane splitPane = new FlippingSplitPane("DomainTop"); splitPane.setLeftComponent(panel); panel = new JPanel(new BorderLayout()); FilterBar<CharacterFacade, DomainFacade> dbar = new FilterBar<>(); dbar.addDisplayableFilter(new SearchFilterPanel()); qDomainButton.setText(LanguageBundle.getString("in_igQualFilter")); //$NON-NLS-1$ dbar.addDisplayableFilter(qDomainButton); domainFilter = dbar; panel.add(dbar, BorderLayout.NORTH); selectionModel = domainTable.getSelectionModel(); selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); domainTable.setAutoCreateColumnsFromModel(false); domainTable.setColumnModel(createDomainColumnModel()); JScrollPane scrollPane = TableUtils.createCheckBoxSelectionPane(domainTable, domainRowHeaderTable); panel.add(scrollPane, BorderLayout.CENTER); box = Box.createHorizontalBox(); box.add(Box.createHorizontalGlue()); box.add(new JLabel(LanguageBundle.getString("in_domRemainDomLabel"))); //$NON-NLS-1$ box.add(Box.createHorizontalStrut(5)); box.add(selectedDomain); box.add(Box.createHorizontalGlue()); panel.add(box, BorderLayout.SOUTH); splitPane.setRightComponent(panel); setTopComponent(splitPane); splitPane = new FlippingSplitPane("DomainBottom"); splitPane.setLeftComponent(deityInfo); splitPane.setRightComponent(domainInfo); setBottomComponent(splitPane); setResizeWeight(0.65); } public DynamicTableColumnModel createDomainColumnModel() { PrefTableColumnModel model = new PrefTableColumnModel("DomainList", 1); TableColumn column = new TableColumn(0); column.setHeaderValue(LanguageBundle.getString("in_domains")); //$NON-NLS-1$ model.addColumn(column, true, 150); column = new TableColumn(1); column.setHeaderValue(LanguageBundle.getString("in_descrip")); //$NON-NLS-1$ model.addColumn(column, false, 150); column = new TableColumn(2); column.setHeaderValue(LanguageBundle.getString("in_source")); //$NON-NLS-1$ model.addColumn(column, true, 150); return model; } @Override public ModelMap createModels(CharacterFacade character) { ModelMap models = new ModelMap(); models.put(DeityTreeViewModel.class, new DeityTreeViewModel(character)); models.put(DomainTableHandler.class, new DomainTableHandler(character)); models.put(SelectDeityAction.class, new SelectDeityAction(character)); models.put(DeityLabelHandler.class, new DeityLabelHandler(character, selectedDeity)); models.put(DomainLabelHandler.class, new DomainLabelHandler(character, selectedDomain)); models.put(DeityInfoHandler.class, new DeityInfoHandler(character)); models.put(DomainInfoHandler.class, new DomainInfoHandler(character)); models.put(DomainRenderer.class, new DomainRenderer(character)); models.put(Handler.class, qualifiedRenderer.createHandler(character)); models.put(QualifiedFilterHandler.class, new QualifiedFilterHandler(character)); return models; } @Override public void restoreModels(ModelMap models) { models.get(DomainLabelHandler.class).install(); models.get(DeityLabelHandler.class).install(); models.get(QualifiedFilterHandler.class).install(); models.get(DomainTableHandler.class).install(); models.get(DomainInfoHandler.class).install(); models.get(DeityInfoHandler.class).install(); models.get(DomainRenderer.class).install(); models.get(SelectDeityAction.class).install(); models.get(Handler.class).install(); deityTable.setTreeViewModel(models.get(DeityTreeViewModel.class)); selectDeity.setAction(models.get(SelectDeityAction.class)); } @Override public void storeModels(ModelMap models) { models.get(DomainLabelHandler.class).uninstall(); models.get(DeityLabelHandler.class).uninstall(); models.get(DomainTableHandler.class).uninstall(); models.get(DomainInfoHandler.class).uninstall(); models.get(DeityInfoHandler.class).uninstall(); models.get(SelectDeityAction.class).uninstall(); models.get(Handler.class).uninstall(); } @Override public TabTitle getTabTitle() { return new TabTitle(Tab.DOMAINS); } @Override public void adviseTodo(String fieldName) { if ("Domains".equals(fieldName)) //$NON-NLS-1$ { if (domainTable.getRowCount() > 0) { domainTable.requestFocusInWindow(); domainTable.getSelectionModel().setSelectionInterval(0, 0); deityTable.getSelectionModel().clearSelection(); } else if (deityTable.getRowCount() > 0) { deityTable.requestFocusInWindow(); deityTable.getSelectionModel().setSelectionInterval(0, 0); } } } private class DomainRenderer extends DefaultTableCellRenderer { private CharacterFacade character; public DomainRenderer(CharacterFacade character) { this.character = character; } public void install() { domainTable.setDefaultRenderer(Object.class, this); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (value instanceof DomainFacade && !character.isQualifiedFor((DomainFacade) value)) { setForeground(UIPropertyContext.getNotQualifiedColor()); } else if (!isSelected) { setForeground(UIPropertyContext.getQualifiedColor()); } if (value instanceof InfoFacade && ((InfoFacade) value).isNamePI()) { setFont(FontManipulation.bold_italic(getFont())); } else { setFont(FontManipulation.plain(getFont())); } return this; } } private class DeityInfoHandler implements ListSelectionListener { private CharacterFacade character; private String text; public DeityInfoHandler(CharacterFacade character) { this.character = character; this.text = ""; //$NON-NLS-1$ } public void install() { deityTable.getSelectionModel().addListSelectionListener(this); deityInfo.setText(text); } public void uninstall() { deityTable.getSelectionModel().removeListSelectionListener(this); } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { int selectedRow = deityTable.getSelectedRow(); if (selectedRow != -1) { Object obj = deityTable.getModel().getValueAt(selectedRow, 0); if (obj instanceof DeityFacade) { text = character.getInfoFactory().getHTMLInfo((DeityFacade) obj); deityInfo.setText(text); } } } } } private class DomainInfoHandler implements ListSelectionListener { private CharacterFacade character; private String text; public DomainInfoHandler(CharacterFacade character) { this.character = character; this.text = ""; //$NON-NLS-1$ } public void install() { domainTable.getSelectionModel().addListSelectionListener(this); domainInfo.setText(text); } public void uninstall() { domainTable.getSelectionModel().removeListSelectionListener(this); } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { if (domainRowHeaderTable.isEditing()) { domainRowHeaderTable.getCellEditor().cancelCellEditing(); } int selectedRow = domainTable.getSelectedRow(); DomainFacade domain = null; if (selectedRow != -1) { domain = (DomainFacade) domainTable.getModel().getValueAt(selectedRow, 0); } if (domain != null) { text = character.getInfoFactory().getHTMLInfo(domain); domainInfo.setText(text); } } } } private class SelectDeityAction extends AbstractAction { private CharacterFacade character; public SelectDeityAction(CharacterFacade character) { super(LanguageBundle.getString("in_select")); //$NON-NLS-1$ this.character = character; } @Override public void actionPerformed(ActionEvent e) { int selectedRow = deityTable.getSelectedRow(); if (selectedRow != -1) { Object rowObj = deityTable.getModel().getValueAt(selectedRow, 0); if (rowObj instanceof DeityFacade) { DeityFacade deity = (DeityFacade) rowObj; character.setDeity(deity); } } } public void install() { deityTable.addActionListener(this); } public void uninstall() { deityTable.removeActionListener(this); } } private class QualifiedFilterHandler { private final Filter<Object, DomainFacade> domainFilter = new Filter<Object, DomainFacade>() { @Override public boolean accept(Object context, DomainFacade element) { return character.isQualifiedFor(element); } }; private final Filter<Object, DeityFacade> deityFilter = new Filter<Object, DeityFacade>() { @Override public boolean accept(Object context, DeityFacade element) { return character.isQualifiedFor(element); } }; private final CharacterFacade character; public QualifiedFilterHandler(CharacterFacade character) { this.character = character; } public void install() { qDomainButton.setFilter(domainFilter); qDeityButton.setFilter(deityFilter); } } private class DomainTableHandler implements FilterHandler { private DomainTableModel tableModel; public DomainTableHandler(CharacterFacade character) { tableModel = new DomainTableModel(character); } public void install() { domainFilter.setFilterHandler(this); tableModel.setFilter(domainFilter); domainTable.setModel(tableModel); domainRowHeaderTable.setModel(tableModel); } public void uninstall() { tableModel.setFilter(null); } @Override public void refilter() { tableModel.refilter(); } @Override public void scrollToTop() { // do nothing } @Override public void setSearchEnabled(boolean enable) { } } private static class DomainLabelHandler implements ReferenceListener<Integer> { private JLabel label; private ReferenceFacade<Integer> ref; public DomainLabelHandler(CharacterFacade character, JLabel label) { ref = character.getRemainingDomainSelectionsRef(); this.label = label; } public void install() { if (ref.get() != null) { label.setText(ref.get().toString()); } ref.addReferenceListener(this); } public void uninstall() { ref.removeReferenceListener(this); } @Override public void referenceChanged(ReferenceEvent<Integer> e) { label.setText(e.getNewReference().toString()); } } private static class DeityLabelHandler implements ReferenceListener<DeityFacade> { private JLabel label; private ReferenceFacade<DeityFacade> ref; public DeityLabelHandler(CharacterFacade character, JLabel label) { ref = character.getDeityRef(); this.label = label; } public void install() { label.setFont(FontManipulation.plain(label.getFont())); if (ref.get() != null) { label.setText(ref.get().toString()); if (ref.get().isNamePI()) { label.setFont(FontManipulation.bold_italic(label.getFont())); } } else { label.setText(""); //$NON-NLS-1$ } ref.addReferenceListener(this); } public void uninstall() { ref.removeReferenceListener(this); } @Override public void referenceChanged(ReferenceEvent<DeityFacade> e) { label.setText(e.getNewReference().toString()); } } private static class DomainTableModel extends FilteredListFacadeTableModel<DomainFacade> { private final ListListener<DomainFacade> listListener = new ListListener<DomainFacade>() { @Override public void elementAdded(ListEvent<DomainFacade> e) { elementsChanged(e); } @Override public void elementRemoved(ListEvent<DomainFacade> e) { elementsChanged(e); } @Override public void elementsChanged(ListEvent<DomainFacade> e) { fireTableRowsUpdated(0, sortedList.getSize() - 1); } @Override public void elementModified(ListEvent<DomainFacade> e) { } }; public DomainTableModel(CharacterFacade character) { super(character); setDelegate(character.getAvailableDomains()); character.getDomains().addListListener(listListener); } @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == -1) { return Boolean.class; } return super.getColumnClass(columnIndex); } @Override protected Object getValueAt(DomainFacade element, int column) { switch (column) { case -1: return character.getDomains().containsElement(element); case 0: return element; case 1: return character.getInfoFactory().getDescription(element); case 2: return element.getSource(); default: return null; } } @Override public int getColumnCount() { return 3; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { if (columnIndex >= 0) { return false; } if (character.getRemainingDomainSelectionsRef().get() > 0) { return true; } DomainFacade domain = sortedList.getElementAt(rowIndex); return character.getDomains().containsElement(domain); } @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { DomainFacade domain = sortedList.getElementAt(rowIndex); Boolean bool = (Boolean) aValue; if (bool) { character.addDomain(domain); } else { character.removeDomain(domain); } } } private static class DeityTreeViewModel implements TreeViewModel<DeityFacade>, DataView<DeityFacade> { private static final ListFacade<TreeView<DeityFacade>> views = new DefaultListFacade<>(Arrays.asList(DeityTreeView.values())); private final List<DefaultDataViewColumn> columns = Arrays.asList(new DefaultDataViewColumn("in_alignLabel", Object.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_domains", String.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_descrip", String.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_pantheon", String.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_favoredWeapon", String.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_sourceLabel", String.class)); //$NON-NLS-1$ private final CharacterFacade character; private InfoFactory infoFactory; public DeityTreeViewModel(CharacterFacade character) { this.character = character; this.infoFactory = character.getInfoFactory(); } @Override public ListFacade<? extends TreeView<DeityFacade>> getTreeViews() { return views; } @Override public int getDefaultTreeViewIndex() { return 0; } @Override public DataView<DeityFacade> getDataView() { return this; } @Override public ListFacade<DeityFacade> getDataModel() { return character.getDataSet().getDeities(); } @Override public Object getData(DeityFacade obj, int column) { switch(column){ case 0: return obj.getAlignment(); case 1: return infoFactory.getDomains(obj); case 2: return infoFactory.getDescription(obj); case 3: return infoFactory.getPantheons(obj); case 4: return infoFactory.getFavoredWeapons(obj); case 5: return obj.getSource(); default: return null; } } @Override public void setData(Object value, DeityFacade element, int column) { } @Override public List<? extends DataViewColumn> getDataColumns() { return columns; } @Override public String getPrefsKey() { return "DeityTree"; //$NON-NLS-1$ } } private enum DeityTreeView implements TreeView<DeityFacade> { NAME("in_deity"), //$NON-NLS-1$ ALIGNMENT_NAME("in_alignmentDeity"), //$NON-NLS-1$ DOMAIN_NAME("in_domainDeity"), //$NON-NLS-1$ PANTHEON_NAME("in_pantheonDeity"), //$NON-NLS-1$ SOURCE_NAME("in_sourceDeity"); //$NON-NLS-1$ private final String name; private DeityTreeView(String name) { this.name = LanguageBundle.getString(name); } @Override public String getViewName() { return name; } @Override public List<TreeViewPath<DeityFacade>> getPaths(DeityFacade pobj) { List<TreeViewPath<DeityFacade>> paths = new ArrayList<>(); switch (this) { case NAME: return Collections.singletonList(new TreeViewPath<>(pobj)); case DOMAIN_NAME: for (String domain : pobj.getDomainNames()) { paths.add(new TreeViewPath<>(pobj, domain)); } return paths; case ALIGNMENT_NAME: return Collections.singletonList(new TreeViewPath<>(pobj, pobj.getAlignment())); case PANTHEON_NAME: for (String pantheon : pobj.getPantheons()) { paths.add(new TreeViewPath<>(pobj, pantheon)); } return paths; case SOURCE_NAME: return Collections.singletonList(new TreeViewPath<>(pobj, pobj.getSourceForNodeDisplay())); default: throw new InternalError(); } } } }