Java tutorial
/* * Copyright 2010 (C) 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 * */ package pcgen.gui2.tabs; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.text.NumberFormat; import java.util.Arrays; import java.util.Collection; import java.util.TreeSet; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.LayoutFocusTraversalPolicy; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import org.apache.commons.lang3.StringUtils; import pcgen.core.PCAlignment; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.CharacterLevelFacade; import pcgen.facade.core.CharacterLevelsFacade; import pcgen.facade.core.ClassFacade; import pcgen.facade.core.DataSetFacade; import pcgen.facade.core.DeityFacade; import pcgen.facade.core.GenderFacade; import pcgen.facade.core.HandedFacade; import pcgen.facade.core.InfoFacade; import pcgen.facade.core.RaceFacade; import pcgen.facade.core.SimpleFacade; import pcgen.facade.core.TodoFacade; 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.dialog.CharacterHPDialog; import pcgen.gui2.dialog.KitSelectionDialog; import pcgen.gui2.dialog.RandomNameDialog; import pcgen.gui2.dialog.SinglePrefDialog; import pcgen.gui2.prefs.CharacterStatsPanel; import pcgen.gui2.tabs.models.CharacterComboBoxModel; import pcgen.gui2.tabs.models.DeferredCharacterComboBoxModel; import pcgen.gui2.tabs.models.FormattedFieldHandler; import pcgen.gui2.tabs.models.TextFieldHandler; import pcgen.gui2.tabs.summary.ClassLevelTableModel; import pcgen.gui2.tabs.summary.InfoPaneHandler; import pcgen.gui2.tabs.summary.LanguageTableModel; import pcgen.gui2.tabs.summary.StatTableModel; import pcgen.gui2.tools.Icons; import pcgen.gui2.tools.Utility; import pcgen.gui2.util.FacadeComboBoxModel; import pcgen.gui2.util.FontManipulation; import pcgen.gui2.util.SignIcon; import pcgen.gui2.util.SignIcon.Sign; import pcgen.gui2.util.SimpleTextIcon; import pcgen.system.LanguageBundle; import pcgen.util.enumeration.Tab; /** * This component displays a basic summary of a character such as name, * alignment, race, class, and stat information. */ @SuppressWarnings("serial") public class SummaryInfoTab extends JPanel implements CharacterInfoTab, TodoHandler { private final TabTitle tabTitle; private final JPanel basicsPanel; private final JPanel todoPanel; private final JPanel scoresPanel; private final JPanel racePanel; private final JPanel classPanel; private final JTextField characterNameField; private final JComboBox characterTypeComboBox; private final JTextField playerNameField; private final JTextField tabLabelField; private final JFormattedTextField ageField; private final JFormattedTextField expField; private final JFormattedTextField nextlevelField; private final JComboBox xpTableComboBox; private final JFormattedTextField expmodField; private final JFormattedTextField addLevelsField; private final JFormattedTextField removeLevelsField; private final JTable statsTable; private final JTable classLevelTable; private final JTable languageTable; private final JComboBox genderComboBox; private final JComboBox handsComboBox; private final JComboBox alignmentComboBox; private final JComboBox deityComboBox; private final JComboBox raceComboBox; private final JComboBox ageComboBox; private final JComboBox classComboBox; private final InfoBoxRenderer infoBoxRenderer; private final ClassBoxRenderer classBoxRenderer; private final JButton generateRollsButton; private final JButton rollMethodButton; private final JButton createMonsterButton; private final JButton expaddButton; private final JButton expsubtractButton; private final JButton addLevelsButton; private final JButton removeLevelsButton; private final JButton hpButton; private final JLabel totalHPLabel; private final JEditorPane infoPane; private final JLabel statTotalLabel; private final JLabel statTotal; private final JLabel modTotalLabel; private final JLabel modTotal; private final JEditorPane todoPane; private final JButton random; private JScrollPane langScroll; SummaryInfoTab() { this.tabTitle = new TabTitle(Tab.SUMMARY); this.basicsPanel = new JPanel(); this.todoPanel = new JPanel(); this.scoresPanel = new JPanel(); this.racePanel = new JPanel(); this.classPanel = new JPanel(); this.characterNameField = new JTextField(); this.characterTypeComboBox = new JComboBox<>(); this.random = new JButton(); FontManipulation.xsmall(random); this.playerNameField = new JTextField(); this.expField = new JFormattedTextField(NumberFormat.getIntegerInstance()); this.nextlevelField = new JFormattedTextField(NumberFormat.getIntegerInstance()); this.xpTableComboBox = new JComboBox<>(); this.expmodField = new JFormattedTextField(NumberFormat.getIntegerInstance()); this.addLevelsField = new JFormattedTextField(NumberFormat.getIntegerInstance()); this.removeLevelsField = new JFormattedTextField(NumberFormat.getIntegerInstance()); this.statsTable = new JTable(); this.classLevelTable = new JTable(); this.languageTable = new JTable(); this.genderComboBox = new JComboBox<>(); this.handsComboBox = new JComboBox<>(); this.alignmentComboBox = new JComboBox<>(); this.deityComboBox = new JComboBox<>(); this.raceComboBox = new JComboBox<>(); this.ageComboBox = new JComboBox<>(); this.classComboBox = new JComboBox<>(); this.tabLabelField = new JTextField(); this.ageField = new JFormattedTextField(NumberFormat.getIntegerInstance()); this.generateRollsButton = new JButton(); this.rollMethodButton = new JButton(); this.createMonsterButton = new JButton(); this.addLevelsButton = new JButton(); this.removeLevelsButton = new JButton(); this.expaddButton = new JButton(); this.expsubtractButton = new JButton(); this.hpButton = new JButton(); this.totalHPLabel = new JLabel(); this.infoPane = new JEditorPane(); this.statTotalLabel = new JLabel(); this.statTotal = new JLabel(); this.modTotalLabel = new JLabel(); this.modTotal = new JLabel(); this.todoPane = new JEditorPane(); this.infoBoxRenderer = new InfoBoxRenderer(); this.classBoxRenderer = new ClassBoxRenderer(); initComponents(); } private void initComponents() { this.setFocusCycleRoot(true); this.setFocusTraversalPolicyProvider(true); this.setFocusTraversalPolicy(new SummaryTabFocusTraversalPolicy()); LanguageTableModel.initializeTable(languageTable); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); setPanelTitle(basicsPanel, LanguageBundle.getString("in_sumCharacterBasics")); //$NON-NLS-1$ basicsPanel.setLayout(new GridBagLayout()); deityComboBox.setRenderer(infoBoxRenderer); raceComboBox.setRenderer(infoBoxRenderer); classComboBox.setRenderer(classBoxRenderer); gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 0.1; gbc.weighty = 0.7; add(basicsPanel, gbc); setPanelTitle(todoPanel, LanguageBundle.getString("in_tipsString")); //$NON-NLS-1$ initTodoPanel(todoPanel); gbc.gridy = 1; gbc.gridheight = GridBagConstraints.REMAINDER; add(todoPanel, gbc); initMiddlePanel(scoresPanel); gbc.gridy = GridBagConstraints.RELATIVE; gbc.weightx = 1; add(scoresPanel, gbc); JPanel rightPanel = new JPanel(); setPanelTitle(racePanel, LanguageBundle.getString("in_raceString")); //$NON-NLS-1$ setPanelTitle(classPanel, LanguageBundle.getString("in_sumClassLevel")); //$NON-NLS-1$ initRightPanel(rightPanel); gbc.weightx = 0.1; gbc.weighty = 1; add(rightPanel, gbc); } private static void setPanelTitle(JComponent panel, String title) { panel.setBorder( BorderFactory.createTitledBorder(null, title, TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION)); } /** * Initialise the "Things to be Done" panel. Creates the required components * and places them in the panel. * * @param panel The panel to be initialised */ private void initTodoPanel(JPanel panel) { panel.setLayout(new BorderLayout()); todoPane.setOpaque(false); todoPane.setContentType("text/html"); //$NON-NLS-1$ todoPane.setEditable(false); JScrollPane scroll = new JScrollPane(todoPane); scroll.setBorder(BorderFactory.createEmptyBorder()); panel.add(scroll, BorderLayout.CENTER); } private void initMiddlePanel(JPanel middlePanel) { middlePanel.setLayout(new GridLayout(2, 1)); JPanel statsPanel = new JPanel(); setPanelTitle(statsPanel, LanguageBundle.getString("in_sumAbilityScores")); //$NON-NLS-1$ statsPanel.setLayout(new BoxLayout(statsPanel, BoxLayout.Y_AXIS)); StatTableModel.initializeTable(statsTable); JScrollPane pane = new JScrollPane(statsTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER) { @Override public Dimension getMaximumSize() { //This prevents the scroll pane from taking up more space than it needs. return super.getPreferredSize(); } }; pane.setBorder(BorderFactory.createEmptyBorder()); JPanel statsBox = new JPanel(); statsBox.setLayout(new BoxLayout(statsBox, BoxLayout.X_AXIS)); statsBox.add(Box.createHorizontalGlue()); statsBox.add(pane); statsBox.add(Box.createHorizontalGlue()); statsPanel.add(statsBox); JPanel statTotalPanel = new JPanel(); statTotalPanel.setLayout(new BoxLayout(statTotalPanel, BoxLayout.X_AXIS)); //this makes box layout use the statTotalPanel to distribute extra space statTotalPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); statTotalPanel.add(Box.createHorizontalGlue()); FontManipulation.title(statTotalLabel); statTotalPanel.add(statTotalLabel); statTotalPanel.add(statTotal); FontManipulation.title(modTotalLabel); statTotalPanel.add(modTotalLabel); statTotalPanel.add(modTotal); statTotalPanel.add(Box.createHorizontalGlue()); generateRollsButton.setText(LanguageBundle.getString("in_sumGenerate_Rolls")); //$NON-NLS-1$ statTotalPanel.add(generateRollsButton); rollMethodButton.setText(LanguageBundle.getString("in_sumRoll_Method")); //$NON-NLS-1$ statTotalPanel.add(Box.createRigidArea(new Dimension(5, 0))); statTotalPanel.add(rollMethodButton); statTotalPanel.add(Box.createHorizontalGlue()); statsPanel.add(statTotalPanel); middlePanel.add(statsPanel); InfoPaneHandler.initializeEditorPane(infoPane); pane = new JScrollPane(infoPane); setPanelTitle(pane, LanguageBundle.getString("in_sumStats")); //$NON-NLS-1$ middlePanel.add(pane); } private void initRightPanel(JPanel rightPanel) { rightPanel.setLayout(new GridBagLayout()); /* * initialize Components */ racePanel.setOpaque(false); classPanel.setOpaque(false); ageField.setHorizontalAlignment(SwingConstants.RIGHT); expField.setHorizontalAlignment(SwingConstants.RIGHT); nextlevelField.setHorizontalAlignment(SwingConstants.RIGHT); nextlevelField.setEnabled(false); expmodField.setHorizontalAlignment(SwingConstants.RIGHT); raceComboBox.setPrototypeDisplayValue("PrototypeDisplayValue"); //$NON-NLS-1$ classComboBox.setPrototypeDisplayValue("PrototypeDisplayValue"); //$NON-NLS-1$ expaddButton.setMargin(new Insets(0, 8, 0, 8)); expsubtractButton.setMargin(new Insets(0, 8, 0, 8)); hpButton.setMargin(new Insets(0, 0, 0, 0)); JPanel expmodPanel = new JPanel(new GridBagLayout()); JPanel levelPanel = new JPanel(); JLabel raceLabel = createLabel("in_sumRace"); //$NON-NLS-1$ JLabel ageLabel = createLabel("in_sumAge"); //$NON-NLS-1$ JLabel classLabel = createLabel("in_sumClass"); //$NON-NLS-1$ JLabel hpLabel = createLabel("in_sumTotalHP"); //$NON-NLS-1$ JLabel expLabel = createLabel("in_sumCurrentXp"); //$NON-NLS-1$ JLabel nextlevelLabel = createLabel("in_sumNextlevel"); //$NON-NLS-1$ JLabel xpTableLabel = createLabel("in_sumXpTable"); //$NON-NLS-1$ JLabel expmodLabel = createLabel("in_sumExpMod"); //$NON-NLS-1$ expmodLabel.setHorizontalAlignment(SwingConstants.CENTER); initLevelPanel(levelPanel); /* * initialize constant variables */ Insets racePanelInsets = racePanel.getInsets(); Insets classPanelInsets = classPanel.getInsets(); /* * racePanel */ GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(racePanelInsets.top, racePanelInsets.left, 0, 0); gbc.gridwidth = 2; rightPanel.add(raceLabel, gbc); gbc.insets = new Insets(racePanelInsets.top, 1, 1, racePanelInsets.right); gbc.gridwidth = GridBagConstraints.REMAINDER; rightPanel.add(raceComboBox, gbc); gbc.insets = new Insets(0, racePanelInsets.left, 0, 1); gbc.gridwidth = 1; rightPanel.add(ageLabel, gbc); gbc.insets = new Insets(1, 1, 1, 1); rightPanel.add(ageField, gbc); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.insets = new Insets(1, 1, 1, racePanelInsets.right); rightPanel.add(ageComboBox, gbc); gbc.insets = new Insets(1, racePanelInsets.left, racePanelInsets.bottom, racePanelInsets.right); rightPanel.add(createMonsterButton, gbc); /* * classPanel */ gbc.gridwidth = 2; gbc.insets = new Insets(classPanelInsets.top, classPanelInsets.left, 0, 0); rightPanel.add(classLabel, gbc); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.insets = new Insets(classPanelInsets.top, 0, 0, classPanelInsets.right); rightPanel.add(classComboBox, gbc); gbc.weighty = 1; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(7, classPanelInsets.left, 0, classPanelInsets.right); rightPanel.add(levelPanel, gbc); gbc.insets.top = 0; gbc.insets.bottom = 10; gbc.weighty = 0; { JPanel hpPanel = new JPanel(new FlowLayout()); hpPanel.add(hpLabel); hpPanel.add(Box.createHorizontalStrut(3)); hpPanel.add(totalHPLabel); hpPanel.add(hpButton); rightPanel.add(hpPanel, gbc); } gbc.insets.bottom = 0; GridBagConstraints leftgbc = new GridBagConstraints(); leftgbc.insets = new Insets(0, classPanelInsets.left, 0, 0); leftgbc.gridwidth = 2; leftgbc.fill = GridBagConstraints.BOTH; GridBagConstraints rightgbc = new GridBagConstraints(); rightgbc.insets = new Insets(0, 0, 0, classPanelInsets.right); rightgbc.gridwidth = GridBagConstraints.REMAINDER; rightgbc.fill = GridBagConstraints.BOTH; rightPanel.add(expLabel, leftgbc); rightPanel.add(expField, rightgbc); rightPanel.add(nextlevelLabel, leftgbc); rightPanel.add(nextlevelField, rightgbc); rightPanel.add(xpTableLabel, leftgbc); rightPanel.add(xpTableComboBox, rightgbc); gbc.insets.top = 10; rightPanel.add(expmodLabel, gbc); { GridBagConstraints gbc2 = new GridBagConstraints(); gbc2.fill = GridBagConstraints.HORIZONTAL; gbc2.weightx = 1.0; gbc2.insets = new Insets(0, 1, 0, 1); expmodPanel.add(expaddButton, gbc2); expmodPanel.add(expsubtractButton, gbc2); } leftgbc.insets.bottom = classPanelInsets.bottom; leftgbc.weightx = 0.3; rightPanel.add(expmodPanel, leftgbc); rightgbc.insets.bottom = classPanelInsets.bottom; rightgbc.weightx = 0.7; rightPanel.add(expmodField, rightgbc); gbc = new GridBagConstraints(); gbc.gridx = gbc.gridy = 0; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.gridheight = 3; gbc.fill = GridBagConstraints.BOTH; rightPanel.add(racePanel, gbc); gbc.gridy = 3; gbc.gridheight = GridBagConstraints.REMAINDER; rightPanel.add(classPanel, gbc); } private void initLevelPanel(JPanel panel) { panel.setLayout(new GridBagLayout()); JLabel addLabel = createLabel("in_sumAddLevels"); //$NON-NLS-1$ JLabel removeLabel = createLabel("in_sumRemoveLevels"); //$NON-NLS-1$ JLabel darrowLabel = new JLabel(Icons.button_arrow_down.getImageIcon()); JLabel uarrowLabel = new JLabel(Icons.button_arrow_up.getImageIcon()); addLevelsButton.setMargin(new Insets(0, 8, 0, 8)); addLevelsField.setValue(1); addLevelsField.setHorizontalAlignment(SwingConstants.RIGHT); removeLevelsButton.setMargin(new Insets(0, 8, 0, 8)); removeLevelsField.setValue(1); removeLevelsField.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc1 = new GridBagConstraints(); GridBagConstraints gbc2 = new GridBagConstraints(); gbc1.weightx = gbc2.weightx = 0.5; gbc1.insets = new Insets(1, 0, 1, 0); gbc2.insets = new Insets(1, 0, 1, 0); gbc2.gridwidth = GridBagConstraints.REMAINDER; panel.add(addLabel, gbc1); panel.add(removeLabel, gbc2); gbc1.ipadx = 30; panel.add(addLevelsField, gbc1); gbc2.ipadx = 30; panel.add(removeLevelsField, gbc2); gbc1.ipadx = 0; panel.add(addLevelsButton, gbc1); gbc2.ipadx = 0; panel.add(removeLevelsButton, gbc2); panel.add(darrowLabel, gbc1); panel.add(uarrowLabel, gbc2); ClassLevelTableModel.initializeTable(classLevelTable); gbc2.weightx = 0; gbc2.weighty = 1; gbc2.fill = GridBagConstraints.BOTH; panel.add(new JScrollPane(classLevelTable), gbc2); } private static JLabel createLabel(String text) { return new JLabel(LanguageBundle.getString(text)); } private void resetBasicsPanel() { basicsPanel.removeAll(); GridBagConstraints gbc = new GridBagConstraints(); { JLabel label = createLabel("in_sumName"); //$NON-NLS-1$ gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(0, 0, 3, 0); basicsPanel.add(label, gbc); random.setText(LanguageBundle.getString("in_randomButton")); //$NON-NLS-1$ random.setMargin(new Insets(0, 0, 0, 0)); gbc.insets = new Insets(0, 2, 3, 2); basicsPanel.add(random, gbc); gbc.insets = new Insets(0, 0, 3, 2); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1.0; basicsPanel.add(characterNameField, gbc); } Insets insets = new Insets(0, 0, 3, 2); Font labelFont = null; addGridBagLayer(basicsPanel, labelFont, insets, "in_sumCharType", characterTypeComboBox); //$NON-NLS-1$ addGridBagLayer(basicsPanel, labelFont, insets, "in_sumPlayer", playerNameField); //$NON-NLS-1$ addGridBagLayer(basicsPanel, labelFont, insets, "in_sumTabLabel", tabLabelField); //$NON-NLS-1$ if (genderComboBox.getModel().getSize() != 0) { addGridBagLayer(basicsPanel, labelFont, insets, "in_sumGender", genderComboBox); //$NON-NLS-1$ } if (handsComboBox.getModel().getSize() != 0) { addGridBagLayer(basicsPanel, labelFont, insets, "in_sumHanded", handsComboBox); //$NON-NLS-1$ } if (alignmentComboBox.getModel().getSize() != 0) { addGridBagLayer(basicsPanel, labelFont, insets, "in_sumAlignment", alignmentComboBox); //$NON-NLS-1$ } if (deityComboBox.getModel().getSize() != 0) { addGridBagLayer(basicsPanel, labelFont, insets, "in_domDeityLabel", deityComboBox); //$NON-NLS-1$ } gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.fill = GridBagConstraints.BOTH; gbc.weighty = 1; gbc.insets = new Insets(6, 2, 2, 2); langScroll = new JScrollPane(languageTable); basicsPanel.add(langScroll, gbc); basicsPanel.revalidate(); } private void addGridBagLayer(JPanel panel, Font font, Insets insets, String text, JComponent comp) { GridBagConstraints gbc = new GridBagConstraints(); JLabel label = new JLabel(LanguageBundle.getString(text)); label.setFont(font); gbc.anchor = GridBagConstraints.WEST; gbc.gridwidth = 2; panel.add(label, gbc); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.fill = GridBagConstraints.BOTH; if (insets != null) { gbc.insets = insets; } panel.add(comp, gbc); } @Override public void adviseTodo(String fieldName) { if ("Name".equals(fieldName)) //$NON-NLS-1$ { characterNameField.requestFocusInWindow(); characterNameField.selectAll(); } else if ("Race".equals(fieldName)) //$NON-NLS-1$ { raceComboBox.requestFocusInWindow(); highlightBorder(raceComboBox); } else if ("Class".equals(fieldName)) //$NON-NLS-1$ { classComboBox.requestFocusInWindow(); highlightBorder(classComboBox); } else if ("Languages".equals(fieldName)) //$NON-NLS-1$ { highlightBorder(langScroll); } else if ("Ability Scores".equals(fieldName)) //$NON-NLS-1$ { deityComboBox.requestFocusInWindow(); deityComboBox.transferFocus(); } } private void highlightBorder(final JComponent comp) { final Border oldBorder = comp.getBorder(); Border highlightBorder = BorderFactory.createLineBorder(Color.GREEN, 3); comp.setBorder(highlightBorder); SwingUtilities.invokeLater(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { // Ignored as we'll exit shortly anyway. } comp.setBorder(oldBorder); }); } @Override public ModelMap createModels(final CharacterFacade character) { ModelMap models = new ModelMap(); models.put(LabelAndFieldHandler.class, new LabelAndFieldHandler(character)); models.put(ComboBoxRendererHandler.class, new ComboBoxRendererHandler(character)); models.put(ComboBoxModelHandler.class, new ComboBoxModelHandler(character)); models.put(RandomNameAction.class, new RandomNameAction(character, (JFrame) SwingUtilities.getWindowAncestor(this))); models.put(ClassLevelTableModel.class, new ClassLevelTableModel(character, classLevelTable, classComboBox)); models.put(GenerateRollsAction.class, new GenerateRollsAction(character)); models.put(RollMethodAction.class, new RollMethodAction(character, (JFrame) SwingUtilities.getWindowAncestor(this))); models.put(CreateMonsterAction.class, new CreateMonsterAction(character, (JFrame) SwingUtilities.getWindowAncestor(this))); models.put(AddLevelsAction.class, new AddLevelsAction(character)); models.put(RemoveLevelsAction.class, new RemoveLevelsAction(character)); models.put(StatTableModel.class, new StatTableModel(character, statsTable)); models.put(LanguageTableModel.class, new LanguageTableModel(character, languageTable)); models.put(InfoPaneHandler.class, new InfoPaneHandler(character, infoPane)); models.put(ExpAddAction.class, new ExpAddAction(character)); models.put(ExpSubtractAction.class, new ExpSubtractAction(character)); models.put(TodoListHandler.class, new TodoListHandler(character)); models.put(HPHandler.class, new HPHandler(character)); return models; } @Override public TabTitle getTabTitle() { return tabTitle; } @Override public void storeModels(ModelMap models) { models.get(LabelAndFieldHandler.class).uninstall(); models.get(ComboBoxModelHandler.class).uninstall(); models.get(LanguageTableModel.class).uninstall(); models.get(ClassLevelTableModel.class).uninstall(); models.get(InfoPaneHandler.class).uninstall(); models.get(StatTableModel.class).uninstall(); models.get(TodoListHandler.class).uninstall(); models.get(GenerateRollsAction.class).uninstall(); models.get(RollMethodAction.class).uninstall(); models.get(HPHandler.class).uninstall(); models.get(ComboBoxRendererHandler.class).uninstall(); } @Override public void restoreModels(ModelMap models) { models.get(LabelAndFieldHandler.class).install(); models.get(ComboBoxRendererHandler.class).install(); models.get(ComboBoxModelHandler.class).install(); models.get(InfoPaneHandler.class).install(); models.get(LanguageTableModel.class).install(); models.get(StatTableModel.class).install(); models.get(ClassLevelTableModel.class).install(); models.get(TodoListHandler.class).install(); models.get(GenerateRollsAction.class).install(); models.get(RollMethodAction.class).install(); models.get(HPHandler.class).install(); random.setAction(models.get(RandomNameAction.class)); generateRollsButton.setAction(models.get(GenerateRollsAction.class)); rollMethodButton.setAction(models.get(RollMethodAction.class)); createMonsterButton.setAction(models.get(CreateMonsterAction.class)); AddLevelsAction addLevelsAction = models.get(AddLevelsAction.class); addLevelsButton.setAction(addLevelsAction); addLevelsField.setAction(addLevelsAction); RemoveLevelsAction removeLevelsAction = models.get(RemoveLevelsAction.class); removeLevelsButton.setAction(removeLevelsAction); removeLevelsField.setAction(removeLevelsAction); ExpAddAction expAddAction = models.get(ExpAddAction.class); expaddButton.setAction(expAddAction); expmodField.setAction(expAddAction); expsubtractButton.setAction(models.get(ExpSubtractAction.class)); addLevelsAction.install(); resetBasicsPanel(); } private class LabelAndFieldHandler { private final LabelHandler statTotalLabelHandler; private final LabelHandler statTotalHandler; private final LabelHandler modTotalLabelHandler; private final LabelHandler modTotalHandler; private final TextFieldHandler charNameHandler; private final TextFieldHandler playerNameHandler; private final TextFieldHandler tabNameHandler; private final FormattedFieldHandler ageHandler; private final FormattedFieldHandler expHandler; private final FormattedFieldHandler nextLevelHandler; LabelAndFieldHandler(final CharacterFacade character) { statTotalLabelHandler = new LabelHandler(statTotalLabel, character.getStatTotalLabelTextRef()); statTotalHandler = new LabelHandler(statTotal, character.getStatTotalTextRef()); modTotalLabelHandler = new LabelHandler(modTotalLabel, character.getModTotalLabelTextRef()); modTotalHandler = new LabelHandler(modTotal, character.getModTotalTextRef()); //Manages the character name text field charNameHandler = new TextFieldHandler(characterNameField, character.getNameRef()) { @Override protected void textChanged(String text) { character.setName(text); } }; //Manages the player name text field. playerNameHandler = new TextFieldHandler(playerNameField, character.getPlayersNameRef()) { @Override protected void textChanged(String text) { character.setPlayersName(text); } }; //Manages the tab name text field. tabNameHandler = new TextFieldHandler(tabLabelField, character.getTabNameRef()) { @Override protected void textChanged(String text) { character.setTabName(text); } }; /* * Handler for the Age field. This listens for and processes both * changes to the value from the character and modifications to the * field made by the user. */ ageHandler = new FormattedFieldHandler(ageField, character.getAgeRef()) { @Override protected void valueChanged(int value) { character.setAge(value); } }; /* * Handler for the Current Experience field. This listens for and * processes both changes to the value from the character and * modifications to the field made by the user. */ expHandler = new FormattedFieldHandler(expField, character.getXPRef()) { @Override protected void valueChanged(int value) { character.setXP(value); } }; /* * Handler for the Next Level field. This is a read-only field so * the handler only listens for changes to the value from the * character. */ nextLevelHandler = new FormattedFieldHandler(nextlevelField, character.getXPForNextLevelRef()) { @Override protected void valueChanged(int value) { //This will never be called } }; } public void install() { charNameHandler.install(); playerNameHandler.install(); tabNameHandler.install(); ageHandler.install(); expHandler.install(); nextLevelHandler.install(); statTotalLabelHandler.install(); statTotalHandler.install(); modTotalLabelHandler.install(); modTotalHandler.install(); } public void uninstall() { charNameHandler.uninstall(); playerNameHandler.uninstall(); tabNameHandler.uninstall(); ageHandler.uninstall(); expHandler.uninstall(); nextLevelHandler.uninstall(); statTotalLabelHandler.uninstall(); statTotalHandler.uninstall(); modTotalLabelHandler.uninstall(); modTotalHandler.uninstall(); } } private class ComboBoxModelHandler { private final CharacterFacade character; private final CharacterComboBoxModel<GenderFacade> genderModel; private final CharacterComboBoxModel<HandedFacade> handsModel; private CharacterComboBoxModel<PCAlignment> alignmentModel; private CharacterComboBoxModel<DeityFacade> deityModel; private final DeferredCharacterComboBoxModel<RaceFacade> raceModel; private final CharacterComboBoxModel<SimpleFacade> ageCatModel; private final FacadeComboBoxModel<ClassFacade> classModel; private final CharacterComboBoxModel<String> xpTableModel; private final CharacterComboBoxModel<String> characterTypeModel; ComboBoxModelHandler(final CharacterFacade character) { this.character = character; DataSetFacade dataset = character.getDataSet(); //initialize character type model characterTypeModel = new CharacterComboBoxModel<String>(dataset.getCharacterTypes(), character.getCharacterTypeRef()) { @Override public void setSelectedItem(Object anItem) { character.setCharacterType((String) anItem); } }; //initialize gender model genderModel = new CharacterComboBoxModel<GenderFacade>(character.getAvailableGenders(), character.getGenderRef()) { @Override public void setSelectedItem(Object anItem) { character.setGender((GenderFacade) anItem); } }; //initialize handed model handsModel = new CharacterComboBoxModel<HandedFacade>(character.getAvailableHands(), character.getHandedRef()) { @Override public void setSelectedItem(Object anItem) { character.setHanded((HandedFacade) anItem); } }; if (!dataset.getAlignments().isEmpty()) { //initialize alignment model alignmentModel = new CharacterComboBoxModel<PCAlignment>(dataset.getAlignments(), character.getAlignmentRef()) { @Override public void setSelectedItem(Object anItem) { character.setAlignment((PCAlignment) anItem); } }; } if (dataset.hasDeityDomain()) { //initialize deity model deityModel = new CharacterComboBoxModel<DeityFacade>(dataset.getDeities(), character.getDeityRef()) { @Override public void setSelectedItem(Object anItem) { character.setDeity((DeityFacade) anItem); } }; } //initialize race model raceModel = new DeferredCharacterComboBoxModel<RaceFacade>(dataset.getRaces(), character.getRaceRef()) { @Override public void commitSelectedItem(Object anItem) { character.setRace((RaceFacade) anItem); } }; //initialize age category model ageCatModel = new CharacterComboBoxModel<SimpleFacade>(character.getAgeCategories(), character.getAgeCategoryRef()) { @Override public void setSelectedItem(Object anItem) { character.setAgeCategory((SimpleFacade) anItem); } }; //initialize XP table model xpTableModel = new CharacterComboBoxModel<String>(dataset.getXPTableNames(), character.getXPTableNameRef()) { @Override public void setSelectedItem(Object anItem) { character.setXPTable((String) anItem); } }; classModel = new FacadeComboBoxModel<>(dataset.getClasses(), null); } public void install() { characterTypeComboBox.setModel(characterTypeModel); genderComboBox.setModel(genderModel); handsComboBox.setModel(handsModel); if (character.getDataSet().getAlignments().isEmpty()) { alignmentComboBox.setVisible(false); } else { alignmentComboBox.setModel(alignmentModel); alignmentComboBox.setVisible(true); } boolean hasDeityDomain = character.getDataSet().hasDeityDomain(); deityComboBox.setVisible(hasDeityDomain); if (hasDeityDomain) { deityComboBox.setModel(deityModel); } raceComboBox.setModel(raceModel); raceComboBox.addFocusListener(raceModel); ageComboBox.setModel(ageCatModel); classComboBox.setModel(classModel); xpTableComboBox.setModel(xpTableModel); } public void uninstall() { raceComboBox.removeFocusListener(raceModel); } } private class ComboBoxRendererHandler { private final CharacterFacade character; ComboBoxRendererHandler(CharacterFacade character) { this.character = character; } public void install() { infoBoxRenderer.setCharacter(character); classBoxRenderer.setCharacter(character); } public void uninstall() { infoBoxRenderer.setCharacter(null); classBoxRenderer.setCharacter(null); } } private static class CharacterComboBoxRenderer extends DefaultListCellRenderer { protected CharacterFacade character; public void setCharacter(CharacterFacade character) { this.character = character; } @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setToolTipText(value == null ? null : value.toString()); return this; } } private static class InfoBoxRenderer extends CharacterComboBoxRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof InfoFacade && !character.isQualifiedFor((InfoFacade) value)) { if (index == -1) {// this is a hack to prevent the combobox from overwriting the text color setText(""); setIcon(new SimpleTextIcon(list, value.toString(), UIPropertyContext.getNotQualifiedColor())); } else { setForeground(UIPropertyContext.getNotQualifiedColor()); } } return this; } } private static class ClassBoxRenderer extends CharacterComboBoxRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof ClassFacade && !character.isQualifiedFor((ClassFacade) value)) { if (index == -1) {// this is a hack to prevent the combobox from overwriting the text color setText(""); setIcon(new SimpleTextIcon(list, value.toString(), UIPropertyContext.getNotQualifiedColor())); } else { setForeground(UIPropertyContext.getNotQualifiedColor()); } } return this; } } private class HPHandler extends AbstractAction implements ReferenceListener<Integer> { private final CharacterFacade character; private final ReferenceFacade<Integer> ref; HPHandler(CharacterFacade character) { this.character = character; this.ref = character.getTotalHPRef(); putValue(NAME, LanguageBundle.getString("in_edit")); //$NON-NLS-1$ } public void install() { hpButton.setAction(this); totalHPLabel.setText(ref.get().toString()); ref.addReferenceListener(this); } public void uninstall() { ref.removeReferenceListener(this); } @Override public void actionPerformed(ActionEvent e) { CharacterHPDialog.showHPDialog(SummaryInfoTab.this, character); } @Override public void referenceChanged(ReferenceEvent<Integer> e) { totalHPLabel.setText(ref.get().toString()); } } private class RandomNameAction extends AbstractAction { private final CharacterFacade character; private final JFrame frame; RandomNameAction(CharacterFacade character, JFrame frame) { this.character = character; this.frame = frame; } @Override public void actionPerformed(ActionEvent e) { String gender = character.getGenderRef().get() != null ? character.getGenderRef().get().toString() : ""; //$NON-NLS-1$ RandomNameDialog dialog = new RandomNameDialog(frame, gender); dialog.setVisible(true); String chosenName = dialog.getChosenName(); if (chosenName != null && !chosenName.isEmpty() && !chosenName.equals(LanguageBundle.getString("in_rndNmDefault"))) //$NON-NLS-1$ { character.setName(chosenName); } String chosenGender = dialog.getGender(); character.setGender(chosenGender); } } /** * Handler for actions from the generate rolls button. Also defines the * appearance of the button. */ private final class GenerateRollsAction extends AbstractAction implements ListListener<CharacterLevelFacade>, ReferenceListener<Integer> { private final CharacterFacade character; GenerateRollsAction(CharacterFacade character) { this.character = character; putValue(NAME, LanguageBundle.getString("in_sumGenerate_Rolls")); //$NON-NLS-1$ update(); } /** * Attach the handler to the screen button. e.g. When the character is * made active. */ public void install() { // Listen to the total levels character.getCharacterLevelsFacade().addListListener(this); // Listen to the roll method character.getRollMethodRef().addReferenceListener(this); } /** * Detach the handler from the on screen button. e.g. when the character * is no longer being displayed. */ public void uninstall() { character.getCharacterLevelsFacade().removeListListener(this); character.getRollMethodRef().removeReferenceListener(this); } @Override public void actionPerformed(ActionEvent e) { character.rollStats(); } /** * Update the state of the button. */ public void update() { setEnabled(character.isStatRollEnabled()); } @Override public void elementAdded(ListEvent<CharacterLevelFacade> e) { update(); } @Override public void elementRemoved(ListEvent<CharacterLevelFacade> e) { update(); } /** * @see pcgen.facade.util.event.ListListener#elementsChanged(ListEvent) */ @Override public void elementsChanged(ListEvent<CharacterLevelFacade> e) { update(); } @Override public void referenceChanged(ReferenceEvent<Integer> e) { update(); } @Override public void elementModified(ListEvent<CharacterLevelFacade> e) { update(); } } /** * Handler for actions from the generate rolls button. Also defines the * appearance of the button. */ private class RollMethodAction extends AbstractAction { private final JFrame parent; private final CharacterFacade character; RollMethodAction(CharacterFacade character, JFrame parent) { putValue(NAME, LanguageBundle.getString("in_sumRoll_Method")); //$NON-NLS-1$ putValue(SHORT_DESCRIPTION, LanguageBundle.getString("in_sumRoll_Method_Tip")); //$NON-NLS-1$ this.parent = parent; this.character = character; } /** * Attach the handler to the screen button. e.g. When the character is * made active. */ public void install() { } /** * Detach the handler from the on screen button. e.g. when the character * is no longer being displayed. */ public void uninstall() { } @Override public void actionPerformed(ActionEvent e) { CharacterStatsPanel charStatsPanel = new CharacterStatsPanel(null); SinglePrefDialog prefsDialog = new SinglePrefDialog(parent, charStatsPanel); charStatsPanel.setParent(prefsDialog); Utility.setComponentRelativeLocation(parent, prefsDialog); prefsDialog.setVisible(true); character.refreshRollMethod(); } } private class CreateMonsterAction extends AbstractAction { private final CharacterFacade character; private final JFrame frame; CreateMonsterAction(CharacterFacade character, JFrame frame) { putValue(NAME, LanguageBundle.getString("in_sumCreateMonster")); //$NON-NLS-1$ putValue(SHORT_DESCRIPTION, LanguageBundle.getString("in_sumCreateMonster_Tip")); //$NON-NLS-1$ this.character = character; this.frame = frame; } @Override public void actionPerformed(ActionEvent e) { KitSelectionDialog kitDialog = new KitSelectionDialog(frame, character); Utility.setComponentRelativeLocation(frame, kitDialog); kitDialog.setVisible(true); } } private class AddLevelsAction extends AbstractAction { private final CharacterFacade character; AddLevelsAction(CharacterFacade character) { this.character = character; putValue(SMALL_ICON, new SignIcon(Sign.Plus)); } @Override public void actionPerformed(ActionEvent e) { ClassFacade c = (ClassFacade) classComboBox.getSelectedItem(); if (c != null) { Number levels = (Number) addLevelsField.getValue(); if (levels.intValue() >= 0) { ClassFacade[] classes = new ClassFacade[levels.intValue()]; Arrays.fill(classes, c); character.addCharacterLevels(classes); } } } public void install() { CharacterLevelsFacade characterLevelsFacade = character.getCharacterLevelsFacade(); int maxLvl = characterLevelsFacade.getSize(); if (maxLvl > 0) { ClassFacade classTaken = characterLevelsFacade .getClassTaken(characterLevelsFacade.getElementAt(maxLvl - 1)); classComboBox.setSelectedItem(classTaken); } } } private class RemoveLevelsAction extends AbstractAction { private final CharacterFacade character; RemoveLevelsAction(CharacterFacade character) { this.character = character; putValue(SMALL_ICON, new SignIcon(Sign.Minus)); } @Override public void actionPerformed(ActionEvent e) { Number levels = (Number) removeLevelsField.getValue(); character.removeCharacterLevels(levels.intValue()); } } /** * Handler for actions from the add experience button. Also defines the * appearance of the button. */ private class ExpAddAction extends AbstractAction { private final CharacterFacade character; ExpAddAction(CharacterFacade character) { this.character = character; putValue(SMALL_ICON, new SignIcon(Sign.Plus)); } @Override public void actionPerformed(ActionEvent e) { Object value = expmodField.getValue(); if (value == null) { return; } int modVal = ((Number) value).intValue(); character.adjustXP(modVal); } } /** * Handler for actions from the subtract experience button. Also defines the * appearance of the button. */ private class ExpSubtractAction extends AbstractAction { private final CharacterFacade character; ExpSubtractAction(CharacterFacade character) { this.character = character; putValue(SMALL_ICON, new SignIcon(Sign.Minus)); } @Override public void actionPerformed(ActionEvent e) { Object value = expmodField.getValue(); if (value == null) { return; } int modVal = ((Number) value).intValue(); character.adjustXP(modVal * -1); } } /** * The Class {@code LabelHandler} manages the text displayed in a * JLabel field for a character. The text will be updated each time a * reference is updated. The handler also knows how to react to install and * uninstall actions when the displayed character changes. */ private class LabelHandler implements ReferenceListener<String> { private ReferenceFacade<String> reference = null; private final JLabel label; /** * Create a new label handler. * * @param label The label to be managed * @param ref the ReferenceFacade that this handler should listen to. */ LabelHandler(JLabel label, ReferenceFacade<String> ref) { this.label = label; setReference(ref); } /** * @param ref The new reference to be watched */ private void setReference(ReferenceFacade<String> ref) { if (reference != null) { reference.removeReferenceListener(this); } reference = ref; if (reference != null) { reference.addReferenceListener(this); label.setText(reference.get()); } } /** * Attach the handler to the on-screen field. e.g. When the character is * made active. */ public void install() { reference.addReferenceListener(this); label.setText(reference.get()); } /** * Detach the handler from the on-screen field. e.g. when the character * is no longer being displayed. */ public void uninstall() { reference.removeReferenceListener(this); } @Override public void referenceChanged(ReferenceEvent<String> e) { label.setText(e.getNewReference()); } } /** * The Class {@code TodoListHandler} manages the text displayed in the * things to be done panel for the character. The text will be updated each * time the character's todo list changes. The handler also knows how to * react to install and uninstall actions when the displayed character * changes. */ @SuppressWarnings("TodoComment") private class TodoListHandler implements ListListener<TodoFacade>, HyperlinkListener { private final CharacterFacade character; private String lastDest = ""; //$NON-NLS-1$ /** * Create a new instance for the character. * * @param character The character being managed. */ TodoListHandler(CharacterFacade character) { this.character = character; } /** * Attach the handler to the on-screen field. e.g. When the character is * made active. */ public void install() { character.getTodoList().addListListener(this); todoPane.addHyperlinkListener(this); lastDest = ""; //$NON-NLS-1$ refreshTodoList(); } /** * Detach the handler from the on-screen field. e.g. when the character * is no longer being displayed. */ public void uninstall() { todoPane.removeHyperlinkListener(this); character.getTodoList().removeListListener(this); } @Override public void elementAdded(ListEvent<TodoFacade> e) { refreshTodoList(); } @Override public void elementRemoved(ListEvent<TodoFacade> e) { refreshTodoList(); } @Override public void elementsChanged(ListEvent<TodoFacade> e) { refreshTodoList(); } @Override public void elementModified(ListEvent<TodoFacade> e) { refreshTodoList(); } /** * Recreate the "Things to be Done" list based on the character's todo * list. */ private void refreshTodoList() { StringBuilder todoText = new StringBuilder("<html><body>"); //$NON-NLS-1$ int i = 1; Collection<TodoFacade> sortedTodos = new TreeSet<>(); character.getTodoList().iterator().forEachRemaining(sortedTodos::add); for (TodoFacade item : sortedTodos) { todoText.append(i++).append(". "); //$NON-NLS-1$ String fieldLoc = item.getTab().name() + "/" + item.getFieldName(); //$NON-NLS-1$ if (StringUtils.isNotEmpty(item.getSubTabName())) { fieldLoc += "/" + item.getSubTabName(); //$NON-NLS-1$ } todoText.append("<a href=\"").append(fieldLoc).append("\">"); //$NON-NLS-1$ //$NON-NLS-2$ if (item.getMessageKey().startsWith("in_")) //$NON-NLS-1$ { todoText.append(LanguageBundle.getFormattedString(item.getMessageKey(), item.getFieldName())); } else { todoText.append(item.getMessageKey()); } todoText.append("</a><br>"); //$NON-NLS-1$ } todoText.append("</body></html>"); //$NON-NLS-1$ todoPane.setText(todoText.toString()); } @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { // We get two messages on a click, so ignore duplicates if (lastDest.equals(e.getDescription())) { lastDest = ""; //$NON-NLS-1$ return; } lastDest = e.getDescription(); firePropertyChange(TodoFacade.SWITCH_TABS, "", e.getDescription()); //$NON-NLS-1$ } } } private class SummaryTabFocusTraversalPolicy extends LayoutFocusTraversalPolicy { @Override public Component getComponentAfter(Container aContainer, Component aComponent) { if (aComponent == deityComboBox) { int column = statsTable.getColumn(StatTableModel.EDITABLE_COLUMN_ID).getModelIndex(); statsTable.editCellAt(0, column); JSpinner spinner = (JSpinner) statsTable.getEditorComponent(); return ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField(); } return super.getComponentAfter(aContainer, aComponent); } @Override public Component getComponentBefore(Container aContainer, Component aComponent) { return super.getComponentBefore(aContainer, aComponent); } } }