From 919e65187f97ef9b438ce6429b4b232d2ac70b38 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Sun, 22 Jun 2025 18:55:05 +0200 Subject: [PATCH] improve addNumberField --- .../gpl/rpg/atcontentstudio/ui/Editor.java | 2228 ++++++++--------- 1 file changed, 1108 insertions(+), 1120 deletions(-) diff --git a/src/com/gpl/rpg/atcontentstudio/ui/Editor.java b/src/com/gpl/rpg/atcontentstudio/ui/Editor.java index cd462a2..4175412 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/Editor.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/Editor.java @@ -1,1120 +1,1108 @@ -package com.gpl.rpg.atcontentstudio.ui; - -import com.gpl.rpg.atcontentstudio.ATContentStudio; -import com.gpl.rpg.atcontentstudio.Notification; -import com.gpl.rpg.atcontentstudio.model.GameDataElement; -import com.gpl.rpg.atcontentstudio.model.Project; -import com.gpl.rpg.atcontentstudio.model.ProjectElementListener; -import com.gpl.rpg.atcontentstudio.model.Workspace; -import com.gpl.rpg.atcontentstudio.model.gamedata.*; -import com.gpl.rpg.atcontentstudio.model.maps.TMXMap; -import com.gpl.rpg.atcontentstudio.utils.WeblateIntegration; -import com.jidesoft.swing.ComboBoxSearchable; -import com.jidesoft.swing.JideBoxLayout; - -import javax.swing.*; -import javax.swing.JSpinner.NumberEditor; -import javax.swing.event.*; -import javax.swing.text.DefaultFormatter; -import javax.swing.text.JTextComponent; -import java.awt.*; -import java.awt.event.*; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.regex.Matcher; - -public abstract class Editor extends JPanel implements ProjectElementListener { - - private static final long serialVersionUID = 241750514033596878L; - private static final FieldUpdateListener nullListener = new FieldUpdateListener() { - @Override - public void valueChanged(JComponent source, Object value) { - } - }; - - public static final String SAVE = "Save"; - public static final String DELETE = "Delete"; - public static final String REVERT = "Revert to original"; - public static final String ALTER = "Alter"; - public static final String GO_TO_ALTERED = "Go to altered"; - - - public static final String READ_ONLY_MESSAGE = - "" + - "This element is not modifiable.
" + - "Click on the \"Alter\" button to create a writable copy." + - "
"; - - public static final String ALTERED_EXISTS_MESSAGE = - "" + - "This element is not modifiable.
" + - "A writable copy exists in this project. Click on \"Go to altered\" to open it." + - "
"; - - public static final String ALTERED_MESSAGE = - "" + - "This element is a writable copy of an element of the referenced game source.
" + - "Take care not to break existing content when modifying it." + - "
"; - - public static final String CREATED_MESSAGE = - "" + - "This element is a creation of yours.
" + - "Do as you please." + - "
"; - - - public String name = "Editor"; - public Icon icon = null; - public GameDataElement target = null; - - public JLabel message = null; - - - public static JTextField addLabelField(JPanel pane, String label, String value) { - return addTextField(pane, label, value, false, nullListener); - } - - public static void addTranslationPane(JPanel pane, final JTextComponent tfComponent, final String initialValue) { - if (Workspace.activeWorkspace.settings.translatorLanguage.getCurrentValue() != null) { - JPanel labelPane = new JPanel(); - labelPane.setLayout(new JideBoxLayout(labelPane, JideBoxLayout.LINE_AXIS, 6)); - final JLabel translateLinkLabel = new JLabel(getWeblateLabelLink(initialValue)); - labelPane.add(translateLinkLabel, JideBoxLayout.FIX); - labelPane.add(new JLabel(" "), JideBoxLayout.FIX); - final JLabel translationStatus = new JLabel("Retrieving..."); - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusUnknownIcon())); - translationStatus.setToolTipText("Connecting to weblate..."); - labelPane.add(translationStatus, JideBoxLayout.VARY); - new Thread() { - public void run() { - WeblateIntegration.WeblateTranslationUnit unit = WeblateIntegration.getTranslationUnit(initialValue); - switch (unit.status) { - case absent: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon())); - translationStatus.setToolTipText("This string isn't managed by weblate (yet)."); - break; - case done: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusGreenIcon())); - translationStatus.setToolTipText("This string is translated on weblate."); - break; - case fuzzy: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusOrangeIcon())); - translationStatus.setToolTipText("This string is translated on weblate, but needs a review."); - break; - case notTranslated: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon())); - translationStatus.setToolTipText("This string isn't translated in your language on weblate yet."); - break; - case warning: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusOrangeIcon())); - translationStatus.setToolTipText("This string is translated on weblate, but triggered some weblate checks."); - break; - case error: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon())); - translationStatus.setToolTipText("Cannot connect to weblate. Check internet connection and firewall settings."); - break; - case notAllowed: - translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusBlueIcon())); - translationStatus.setToolTipText("You have not allowed ATCS to access to internet. You can change this in the workspace settings."); - break; - } - translationStatus.setText(unit.translatedText); - } - - }.start(); - pane.add(labelPane, JideBoxLayout.FIX); - tfComponent.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void removeUpdate(DocumentEvent e) { - translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))); - translateLinkLabel.revalidate(); - translateLinkLabel.repaint(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))); - translateLinkLabel.revalidate(); - translateLinkLabel.repaint(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))); - translateLinkLabel.revalidate(); - translateLinkLabel.repaint(); - } - }); - translateLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); - translateLinkLabel.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { - try { - Desktop.getDesktop().browse(new URI(WeblateIntegration.getWeblateLabelURI(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n"))))); - } catch (IOException e1) { - e1.printStackTrace(); - } catch (URISyntaxException e1) { - e1.printStackTrace(); - } - } - } - }); - } - } - - public static JTextField addTranslatableTextField(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { - final JTextField tfField = addTextField(pane, label, initialValue, editable, listener); - addTranslationPane(pane, tfField, initialValue); - return tfField; - } - - public static String getWeblateLabelLink(String text) { - return "Translate on weblate"; - } - - public static JTextField addTextField(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { - final JTextField tfField = new JTextField(initialValue); - addTextComponent(pane, label, editable, listener, tfField, false, false); - tfField.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - listener.valueChanged(tfField, tfField.getText()); - } - }); - return tfField; - } - public static T addTextComponent(JPanel pane, String label, boolean editable, final FieldUpdateListener listener, T tfField, boolean specialNewLinesHandling, boolean scrollable) { - JPanel tfPane = new JPanel(); - tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6)); - JLabel tfLabel = new JLabel(label); - tfPane.add(tfLabel, JideBoxLayout.FIX); - tfField.setEditable(editable); - JComponent component; - if (scrollable){ - component = new JScrollPane(tfField); - }else{ - component = tfField; - } - tfPane.add(component, JideBoxLayout.VARY); - JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); - tfPane.add(nullify, JideBoxLayout.FIX); - nullify.setEnabled(editable); - pane.add(tfPane, JideBoxLayout.FIX); - - nullify.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - tfField.setText(""); - listener.valueChanged(tfField, null); - } - }); - tfField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void removeUpdate(DocumentEvent e) { - String text = tfField.getText(); - if(specialNewLinesHandling) text = text.replaceAll("\n", Matcher.quoteReplacement("\n")); - listener.valueChanged(tfField, text); - } - - @Override - public void insertUpdate(DocumentEvent e) { - String text = tfField.getText(); - if(specialNewLinesHandling) text = text.replaceAll("\n", Matcher.quoteReplacement("\n")); - listener.valueChanged(tfField, text); - } - - @Override - public void changedUpdate(DocumentEvent e) { - String text = tfField.getText(); - if(specialNewLinesHandling) text = text.replaceAll("\n", Matcher.quoteReplacement("\n")); - listener.valueChanged(tfField, text); - } - }); - return tfField; - } - - - public static JTextArea addTranslatableTextArea(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { - final JTextArea tfArea = addTextArea(pane, label, initialValue, editable, listener); - addTranslationPane(pane, tfArea, initialValue); - return tfArea; - } - - public static JTextArea addTextArea(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { - String text = initialValue == null ? "" : initialValue.replaceAll("\\n", "\n"); - final JTextArea tfArea = new JTextArea(text); - tfArea.setRows(2); - tfArea.setLineWrap(true); - tfArea.setWrapStyleWord(true); - - addTextComponent(pane, label, editable, listener, tfArea, true, true); - return tfArea; - } - -// public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, boolean allowNegatives, boolean editable) { -// return addIntegerField(pane, label, initialValue, allowNegatives, editable, nullListener); -// } - - public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, boolean allowNegatives, boolean editable, final FieldUpdateListener listener) { - return addIntegerField(pane, label, initialValue, 0, allowNegatives, editable, listener); - } - - public static > JSpinner addNumberField(JPanel pane, String label, boolean editable, final FieldUpdateListener listener, T minimum, T maximum, Number stepSize, Number value, Number defaultValue) { - JPanel tfPane = new JPanel(); - tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6)); - JLabel tfLabel = new JLabel(label); - tfPane.add(tfLabel, JideBoxLayout.FIX); - if (!(((minimum == null) || (((Comparable)minimum).compareTo(value) <= 0)) && - ((maximum == null) || (((Comparable)maximum).compareTo(value) >= 0)))) { - try{ - throw new IllegalArgumentException("Value for number field outside of range: %s <= %s <= %s".formatted(minimum, value, maximum)); - }catch (IllegalArgumentException e){ - e.printStackTrace(); - value = defaultValue; - } - } - final JSpinner spinner = new JSpinner(new SpinnerNumberModel(value, minimum, maximum, stepSize)); - ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); - spinner.setEnabled(editable); - ((DefaultFormatter) ((NumberEditor) spinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true); - tfPane.add(spinner, JideBoxLayout.VARY); - JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); - tfPane.add(nullify, JideBoxLayout.FIX); - nullify.setEnabled(editable); - pane.add(tfPane, JideBoxLayout.FIX); - spinner.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - listener.valueChanged(spinner, spinner.getValue()); - } - }); - nullify.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - spinner.setValue(0); - listener.valueChanged(spinner, null); - } - }); - return spinner; - } - public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, Integer defaultValue, boolean allowNegatives, boolean editable, final FieldUpdateListener listener) { - int value = initialValue != null ? initialValue : defaultValue; - int minimum = allowNegatives ? Integer.MIN_VALUE : 0; - int maxValue = Integer.MAX_VALUE; - return addNumberField(pane, label, editable, listener, minimum, maxValue, 1, value, defaultValue); - } - - - private static final String percent = "%"; - private static final String ratio = "x/y"; - - public static JComponent addChanceField(JPanel pane, String label, String initialValue, String defaultValue, boolean editable, final FieldUpdateListener listener) { - int defaultChance = 1; - int defaultMaxChance = 100; - if (defaultValue != null) { - if (defaultValue.contains("/")) { - int c = defaultValue.indexOf('/'); - try { - defaultChance = Integer.parseInt(defaultValue.substring(0, c)); - } catch (NumberFormatException nfe) { - } - try { - defaultMaxChance = Integer.parseInt(defaultValue.substring(c + 1)); - } catch (NumberFormatException nfe) { - } - } else { - try { - defaultChance = Integer.parseInt(defaultValue); - } catch (NumberFormatException nfe) { - } - } - } - - boolean currentFormIsRatio = true; - int chance = defaultChance; - int maxChance = defaultMaxChance; - if (initialValue != null) { - if (initialValue.contains("/")) { - int c = initialValue.indexOf('/'); - try { - chance = Integer.parseInt(initialValue.substring(0, c)); - } catch (NumberFormatException nfe) { - } - try { - maxChance = Integer.parseInt(initialValue.substring(c + 1)); - } catch (NumberFormatException nfe) { - } - } else { - try { - chance = Integer.parseInt(initialValue); - currentFormIsRatio = false; - } catch (NumberFormatException nfe) { - } - } - } - - final JPanel tfPane = new JPanel(); - tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6)); - JLabel tfLabel = new JLabel(label); - tfPane.add(tfLabel, JideBoxLayout.FIX); - - final JComboBox entryTypeBox = new JComboBox(new String[]{percent, ratio}); - if (currentFormIsRatio) { - entryTypeBox.setSelectedItem(ratio); - } else { - entryTypeBox.setSelectedItem(percent); - } - entryTypeBox.setEnabled(editable); - tfPane.add(entryTypeBox, JideBoxLayout.FIX); - /////////////////////////////////////////////////////////////////////////////////////////////////// make sure "chance" is between 1 and 100. If lower than 1 get 1. If higher than 100, get chance/maxChance * 100... Then do the same with defaultChance, in case no value exist. - final SpinnerNumberModel percentModel = new SpinnerNumberModel(initialValue != null ? ((chance > 1 ? chance : 1) < 100 ? chance : (chance * 100 / maxChance)) : ((defaultChance > 1 ? defaultChance : 1) < 100 ? defaultChance : (defaultChance * 100 / defaultMaxChance)), 1, 100, 1); - final SpinnerNumberModel ratioChanceModel = new SpinnerNumberModel(initialValue != null ? chance : defaultChance, 1, Integer.MAX_VALUE, 1); - - final JSpinner chanceSpinner = new JSpinner(currentFormIsRatio ? ratioChanceModel : percentModel); - if (!currentFormIsRatio) - ((JSpinner.DefaultEditor) chanceSpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); - chanceSpinner.setEnabled(editable); - ((DefaultFormatter) ((NumberEditor) chanceSpinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true); - tfPane.add(chanceSpinner, JideBoxLayout.FLEXIBLE); - - final JLabel ratioLabel = new JLabel("/"); - tfPane.add(ratioLabel, JideBoxLayout.FIX); - - final JSpinner maxChanceSpinner = new JSpinner(new SpinnerNumberModel(initialValue != null ? maxChance : defaultMaxChance, 1, Integer.MAX_VALUE, 1)); - ((JSpinner.DefaultEditor) maxChanceSpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); - maxChanceSpinner.setEnabled(editable); - ((DefaultFormatter) ((NumberEditor) maxChanceSpinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true); - tfPane.add(maxChanceSpinner, JideBoxLayout.FLEXIBLE); - - if (!currentFormIsRatio) { - ratioLabel.setVisible(false); - maxChanceSpinner.setVisible(false); - tfPane.revalidate(); - tfPane.repaint(); - } - - final JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); - tfPane.add(nullify, JideBoxLayout.FIX); - nullify.setEnabled(editable); - pane.add(tfPane, JideBoxLayout.FIX); - - entryTypeBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (entryTypeBox.getSelectedItem() == percent) { - int chance = ((Integer) chanceSpinner.getValue()); - int maxChance = ((Integer) maxChanceSpinner.getValue()); - chance *= 100; - chance /= maxChance; - chance = Math.max(0, Math.min(100, chance)); - chanceSpinner.setModel(percentModel); - chanceSpinner.setValue(chance); - ((JSpinner.DefaultEditor) chanceSpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); - ratioLabel.setVisible(false); - maxChanceSpinner.setVisible(false); - tfPane.revalidate(); - tfPane.repaint(); - listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString()); - } else if (entryTypeBox.getSelectedItem() == ratio) { - int chance = ((Integer) chanceSpinner.getValue()); - chanceSpinner.setModel(ratioChanceModel); - chanceSpinner.setValue(chance); - maxChanceSpinner.setValue(100); - ratioLabel.setVisible(true); - maxChanceSpinner.setVisible(true); - tfPane.revalidate(); - tfPane.repaint(); - listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString() + "/" + maxChanceSpinner.getValue().toString()); - } - } - }); - chanceSpinner.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - if (entryTypeBox.getSelectedItem() == percent) { - listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString()); - } else if (entryTypeBox.getSelectedItem() == ratio) { - listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString() + "/" + maxChanceSpinner.getValue().toString()); - } - } - }); - maxChanceSpinner.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString() + "/" + maxChanceSpinner.getValue().toString()); - } - }); - nullify.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - chanceSpinner.setValue(1); - listener.valueChanged(chanceSpinner, null); - } - }); - return chanceSpinner; - } - -// public static JSpinner addDoubleField(JPanel pane, String label, Double initialValue, boolean editable) { -// return addDoubleField(pane, label, initialValue, editable, nullListener); -// } - - public static JSpinner addDoubleField(JPanel pane, String label, Double initialValue, boolean editable, final FieldUpdateListener listener) { - double minimum = 0.0d; - double defaultValue = 0.0d; - double value = initialValue != null ? initialValue : minimum; - double maximum = new Float(Float.MAX_VALUE).doubleValue(); - return addNumberField(pane, label, editable, listener, minimum, maximum, 1.0d, value, defaultValue); - } - - public static IntegerBasedCheckBox addIntegerBasedCheckBox(JPanel pane, String label, Integer initialValue, boolean editable) { - return addIntegerBasedCheckBox(pane, label, initialValue, editable, nullListener); - } - - public static IntegerBasedCheckBox addIntegerBasedCheckBox(JPanel pane, String label, Integer initialValue, boolean editable, final FieldUpdateListener listener) { - JPanel ibcbPane = new JPanel(); - ibcbPane.setLayout(new BorderLayout()); - final IntegerBasedCheckBox ibcb = new IntegerBasedCheckBox(); - ibcb.setText(label); - ibcb.setIntegerValue(initialValue); - ibcb.setEnabled(editable); - ibcbPane.add(ibcb, BorderLayout.WEST); - ibcbPane.add(new JPanel(), BorderLayout.CENTER); - pane.add(ibcbPane, JideBoxLayout.FIX); - ibcb.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - listener.valueChanged(ibcb, ibcb.getIntegerValue()); - } - }); - return ibcb; - } - - public static BooleanBasedCheckBox addBooleanBasedCheckBox(JPanel pane, String label, Boolean initialValue, boolean editable, final FieldUpdateListener listener) { - JPanel bbcbPane = new JPanel(); - bbcbPane.setLayout(new BorderLayout()); - final BooleanBasedCheckBox bbcb = new BooleanBasedCheckBox(); - bbcb.setText(label); - bbcb.setBooleanValue(initialValue); - bbcb.setEnabled(editable); - bbcbPane.add(bbcb, BorderLayout.WEST); - bbcbPane.add(new JPanel(), BorderLayout.CENTER); - pane.add(bbcbPane, JideBoxLayout.FIX); - bbcb.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - listener.valueChanged(bbcb, bbcb.isSelected()); - } - }); - return bbcb; - } - - @SuppressWarnings("rawtypes") - public static JComboBox addEnumValueBox(JPanel pane, String label, Enum[] values, Enum initialValue, boolean writable) { - return addEnumValueBox(pane, label, values, initialValue, writable, new FieldUpdateListener() { - @Override - public void valueChanged(JComponent source, Object value) { - } - }); - } - - @SuppressWarnings("rawtypes") - public static JComboBox addEnumValueBox(JPanel pane, String label, Enum[] values, Enum initialValue, boolean writable, final FieldUpdateListener listener) { - JPanel comboPane = new JPanel(); - comboPane.setLayout(new JideBoxLayout(comboPane, JideBoxLayout.LINE_AXIS, 6)); - JLabel comboLabel = new JLabel(label); - comboPane.add(comboLabel, JideBoxLayout.FIX); - @SuppressWarnings("unchecked") final JComboBox enumValuesCombo = new JComboBox(values); - enumValuesCombo.setEnabled(writable); - enumValuesCombo.setSelectedItem(initialValue); - comboPane.add(enumValuesCombo, JideBoxLayout.VARY); - enumValuesCombo.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - listener.valueChanged(enumValuesCombo, e.getItem()); - } - } - }); - JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); - comboPane.add(nullify, JideBoxLayout.FIX); - nullify.setEnabled(writable); - nullify.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - enumValuesCombo.setSelectedItem(null); - listener.valueChanged(enumValuesCombo, null); - } - }); - - pane.add(comboPane, JideBoxLayout.FIX); - return enumValuesCombo; - } - - - public MyComboBox addNPCBox(JPanel pane, Project proj, String label, NPC npc, boolean writable, FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, npc) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public NPC getTypedElementAt(int index) { - return project.getNPC(index); - } - - @Override - public int getSize() { - return project.getNPCCount() + 1; - } - }; - return addGDEBox(pane, label, npc, NPC.class, comboModel, writable, listener); - } - - public MyComboBox addActorConditionBox(JPanel pane, Project proj, String label, ActorCondition acond, boolean writable, FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, acond) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public ActorCondition getTypedElementAt(int index) { - return project.getActorCondition(index); - } - - @Override - public int getSize() { - return project.getActorConditionCount() + 1; - } - }; - return addGDEBox(pane, label, acond, ActorCondition.class, comboModel, writable, listener); - } - - public MyComboBox addItemBox(JPanel pane, Project proj, String label, Item item, boolean writable, FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, item) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public Item getTypedElementAt(int index) { - return project.getItem(index); - } - - @Override - public int getSize() { - return project.getItemCount() + 1; - } - }; - return addGDEBox(pane, label, item, Item.class, comboModel, writable, listener); - } - - public MyComboBox addItemCategoryBox(JPanel pane, Project proj, String label, ItemCategory ic, boolean writable, FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, ic) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public ItemCategory getTypedElementAt(int index) { - return project.getItemCategory(index); - } - - @Override - public int getSize() { - return project.getItemCategoryCount() + 1; - } - }; - return addGDEBox(pane, label, ic, ItemCategory.class, comboModel, writable, listener); - } - - public MyComboBox addQuestBox(JPanel pane, Project proj, String label, Quest quest, boolean writable, FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, quest) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public Quest getTypedElementAt(int index) { - return project.getQuest(index); - } - - @Override - public int getSize() { - return project.getQuestCount() + 1; - } - }; - return addGDEBox(pane, label, quest, Quest.class, comboModel, writable, listener); - } - - public MyComboBox addDroplistBox(JPanel pane, Project proj, String label, Droplist droplist, boolean writable, FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, droplist) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public Droplist getTypedElementAt(int index) { - return project.getDroplist(index); - } - - @Override - public int getSize() { - return project.getDroplistCount() + 1; - } - }; - return addGDEBox(pane, label, droplist, Droplist.class, comboModel, writable, listener); - } - - public MyComboBox addDialogueBox(JPanel pane, Project proj, String label, Dialogue dialogue, boolean writable, final FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, dialogue) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public Dialogue getTypedElementAt(int index) { - return project.getDialogue(index); - } - - @Override - public int getSize() { - return project.getDialogueCount() + 1; - } - }; - return addGDEBox(pane, label, dialogue, Dialogue.class, comboModel, writable, listener); - } - - public MyComboBox addMapBox(JPanel pane, Project proj, String label, TMXMap map, boolean writable, final FieldUpdateListener listener) { - final GDEComboModel comboModel = new GDEComboModel(proj, map) { - private static final long serialVersionUID = 2638082961277241764L; - - @Override - public TMXMap getTypedElementAt(int index) { - return project.getMap(index); - } - - @Override - public int getSize() { - return project.getMapCount() + 1; - } - }; - return addGDEBox(pane, label, map, TMXMap.class, comboModel, writable, listener); - } - - @SuppressWarnings("unchecked") - public MyComboBox addGDEBox(JPanel pane, String label, GameDataElement gde, final Class dataClass, final GDEComboModel comboModel, final boolean writable, final FieldUpdateListener listener) { - JPanel gdePane = new JPanel(); - gdePane.setLayout(new JideBoxLayout(gdePane, JideBoxLayout.LINE_AXIS, 6)); - JLabel gdeLabel = new JLabel(label); - gdePane.add(gdeLabel, JideBoxLayout.FIX); - final MyComboBox gdeBox = new MyComboBox(dataClass, comboModel); - gdeBox.setRenderer(new GDERenderer(false, writable)); - new ComboBoxSearchable(gdeBox) { - @Override - protected String convertElementToString(Object object) { - if (object == null) return "none"; - else return ((GameDataElement) object).getDesc(); - } - }; - gdeBox.setEnabled(writable); - gdePane.add(gdeBox, JideBoxLayout.VARY); - final JButton goToGde = new JButton((Icon) ((gde != null) ? new ImageIcon(gde.getIcon()) : (writable ? new ImageIcon(DefaultIcons.getCreateIcon()) : null))); - goToGde.setEnabled(gde != null || writable); - goToGde.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - GameDataElement selected = ((GameDataElement) comboModel.getSelectedItem()); - if (selected != null) { - ATContentStudio.frame.openEditor(((GameDataElement) comboModel.getSelectedItem())); - ATContentStudio.frame.selectInTree((GameDataElement) comboModel.getSelectedItem()); - } else if (writable) { - JSONCreationWizard wizard = new JSONCreationWizard(((GameDataElement) target).getProject(), dataClass); - wizard.addCreationListener(new JSONCreationWizard.CreationCompletedListener() { - - @Override - public void elementCreated(JSONElement created) { - gdeBox.setSelectedItem(created); - } - }); - wizard.setVisible(true); - } - } - }); - gdePane.add(goToGde, JideBoxLayout.FIX); - gdeBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (gdeBox.getModel().getSelectedItem() == null) { - goToGde.setIcon((writable ? new ImageIcon(DefaultIcons.getCreateIcon()) : null)); - goToGde.setEnabled(writable); - } else { - goToGde.setIcon(new ImageIcon(((GameDataElement) comboModel.getSelectedItem()).getIcon())); - goToGde.setEnabled(true); - } - listener.valueChanged(gdeBox, gdeBox.getModel().getSelectedItem()); - } - }); - JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); - gdePane.add(nullify, JideBoxLayout.FIX); - nullify.setEnabled(writable); - nullify.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gdeBox.setSelectedItem(null); - } - }); - pane.add(gdePane, JideBoxLayout.FIX); - - return gdeBox; - } - - public JComboBox addQuestStageBox(JPanel pane, Project proj, String label, Integer initialValue, boolean writable, final FieldUpdateListener listener, Quest quest, @SuppressWarnings("rawtypes") final JComboBox questSelectionBox) { - JPanel gdePane = new JPanel(); - gdePane.setLayout(new JideBoxLayout(gdePane, JideBoxLayout.LINE_AXIS, 6)); - JLabel gdeLabel = new JLabel(label); - gdePane.add(gdeLabel, JideBoxLayout.FIX); - - QuestStage initial = null; - if (quest != null) { - initial = quest.getStage(initialValue); - } - final QuestStageComboModel comboModel = new QuestStageComboModel(proj, initial, quest); - final JComboBox combo = new JComboBox(comboModel); - combo.setRenderer(new GDERenderer(false, writable)); - new ComboBoxSearchable(combo) { - @Override - protected String convertElementToString(Object object) { - if (object == null) return "none"; - else return ((GameDataElement) object).getDesc(); - } - }; - questSelectionBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (comboModel.selected != null) { - Editor.this.target.removeBacklink(comboModel.selected); - } - Quest newQuest = (Quest) questSelectionBox.getSelectedItem(); - comboModel.changeQuest(newQuest); - combo.revalidate(); - } - }); - combo.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - listener.valueChanged(combo, comboModel.selected == null ? null : comboModel.selected.progress); - } - }); - - - combo.setEnabled(writable); - gdePane.add(combo, JideBoxLayout.VARY); - - pane.add(gdePane, JideBoxLayout.FIX); - - return combo; - } - - - @SuppressWarnings({"rawtypes"}) - public JList addBacklinksList(JPanel pane, GameDataElement gde) { - return addBacklinksList(pane, gde, "Elements linking to this one"); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public JList addBacklinksList(JPanel pane, GameDataElement gde, String title) { - final JList list = new JList(new GDEBacklinksListModel(gde)); - list.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - ATContentStudio.frame.openEditor((GameDataElement) list.getSelectedValue()); - ATContentStudio.frame.selectInTree((GameDataElement) list.getSelectedValue()); - } - } - }); - list.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - ATContentStudio.frame.openEditor((GameDataElement) list.getSelectedValue()); - ATContentStudio.frame.selectInTree((GameDataElement) list.getSelectedValue()); - } - } - }); - list.setCellRenderer(new GDERenderer(true, false)); - CollapsiblePanel colPane = new CollapsiblePanel(title); - colPane.setLayout(new JideBoxLayout(colPane, JideBoxLayout.PAGE_AXIS)); - colPane.add(new JScrollPane(list), JideBoxLayout.FIX); - colPane.add(new JPanel(), JideBoxLayout.FIX); - if (gde.getBacklinks() == null || gde.getBacklinks().isEmpty()) { - colPane.collapse(); - } - pane.add(colPane, JideBoxLayout.FIX); - return list; - } - - public static abstract class GDEComboModel extends AbstractListModel implements ComboBoxModel { - - private static final long serialVersionUID = -5854574666510314715L; - - public Project project; - public E selected; - - public GDEComboModel(Project proj, E initial) { - this.project = proj; - this.selected = initial; - } - - @Override - public abstract int getSize(); - - @Override - public E getElementAt(int index) { - if (index == 0) { - return null; - } - return getTypedElementAt(index - 1); - } - - public abstract E getTypedElementAt(int index); - - @SuppressWarnings("unchecked") - @Override - public void setSelectedItem(Object anItem) { - selected = (E) anItem; - } - - @Override - public Object getSelectedItem() { - return selected; - } - - public void itemAdded(E item, int index) { - fireIntervalAdded(this, index, index); - } - - public void itemRemoved(E item, int index) { - fireIntervalRemoved(this, index, index); - } - - } - - public static class GDERenderer extends DefaultListCellRenderer { - - private static final long serialVersionUID = 6819681566800482793L; - - private boolean includeType; - private boolean writable; - - public GDERenderer(boolean includeType, boolean writable) { - super(); - this.includeType = includeType; - this.writable = writable; - } - - @SuppressWarnings("rawtypes") - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value == null) { - label.setText("None" + (writable ? ". Click on the button to create one." : "")); - } else { - if (includeType && ((GameDataElement) value).getDataType() != null) { - if (value instanceof QuestStage) { - String text = ((GameDataElement) value).getDesc(); - if (text.length() > 60) { - text = text.substring(0, 57) + "..."; - } - label.setText(((GameDataElement) value).getDataType().toString() + "/" + ((Quest) ((QuestStage) value).parent).id + "#" + ((QuestStage) value).progress + ":" + text); - } else { - label.setText(((GameDataElement) value).getDataType().toString() + "/" + ((GameDataElement) value).getDesc()); - } - } else { - if (value instanceof QuestStage) { - String text = ((GameDataElement) value).getDesc(); - if (text.length() > 60) { - text = text.substring(0, 57) + "..."; - } - label.setText(text); - } else { - label.setText(((GameDataElement) value).getDesc()); - } - } - if (((GameDataElement) value).getIcon() == null) { - Notification.addError("Unable to find icon for " + ((GameDataElement) value).getDesc()); - } else { - label.setIcon(new ImageIcon(((GameDataElement) value).getIcon())); - } - } - return label; - } - - } - - public static class QuestStageComboModel extends AbstractListModel implements ComboBoxModel { - - private static final long serialVersionUID = -5854574666510314715L; - - public Project project; - public Quest currentQuest; - public QuestStage selected; - - public QuestStageComboModel(Project proj, QuestStage initial, Quest quest) { - this.project = proj; - this.currentQuest = quest; - this.selected = initial; - } - - @Override - public int getSize() { - if (currentQuest == null) return 1; - return currentQuest.stages.size() + 1; - } - - @Override - public QuestStage getElementAt(int index) { - if (index == 0) { - return null; - } - return currentQuest.stages.get(index - 1); - } - - @Override - public void setSelectedItem(Object anItem) { - selected = (QuestStage) anItem; - } - - @Override - public Object getSelectedItem() { - return selected; - } - - public void itemAdded(QuestStage item, int index) { - fireIntervalAdded(this, index, index); - } - - public void itemRemoved(QuestStage item, int index) { - fireIntervalRemoved(this, index, index); - } - - public void changeQuest(Quest newQuest) { - int size = getSize(); - currentQuest = null; - selected = null; - fireIntervalRemoved(this, 1, size); - currentQuest = newQuest; - fireIntervalAdded(this, 1, getSize()); - } - - } - - - public static class GDEBacklinksListModel implements ListenerCollectionModel { - - GameDataElement source; - - public GDEBacklinksListModel(GameDataElement source) { - super(); - this.source = source; - source.addBacklinkListener(new GameDataElement.BacklinksListener() { - @Override - public void backlinkRemoved(GameDataElement gde) { - fireListChanged(); - } - - @Override - public void backlinkAdded(GameDataElement gde) { - fireListChanged(); - } - }); - } - - @Override - public Collection getElements() { - return source.getBacklinks(); - } - - List listeners = new CopyOnWriteArrayList(); - - @Override - public List getListeners() { - return listeners; - } - - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public class MyComboBox extends JComboBox implements ProjectElementListener { - - private static final long serialVersionUID = -4184228604170642567L; - - Class dataType; - - public MyComboBox(Class dataType, ComboBoxModel model) { - super(model); - this.dataType = dataType; - Editor.this.addElementListener(dataType, this); - } - - @Override - public void elementAdded(GameDataElement added, int index) { - ((GDEComboModel) getModel()).itemAdded(added, index); - } - - @Override - public void elementRemoved(GameDataElement removed, int index) { - ((GDEComboModel) getModel()).itemRemoved(removed, index); - } - - @Override - public Class getDataType() { - return dataType; - } - - } - - public abstract void targetUpdated(); - - - transient Map, List> projectElementListeners = new HashMap, List>(); - - public void addElementListener(Class interestingType, ProjectElementListener listener) { - if (projectElementListeners.get(interestingType) == null) { - projectElementListeners.put(interestingType, new ArrayList()); - target.getProject().addElementListener(interestingType, this); - } - projectElementListeners.get(interestingType).add(listener); - } - - public void removeElementListener(ProjectElementListener listener) { - if (listener == null) return; - if (projectElementListeners.get(listener.getDataType()) != null) { - projectElementListeners.get(listener.getDataType()).remove(listener); - if (projectElementListeners.get(listener.getDataType()).isEmpty()) { - target.getProject().removeElementListener(listener.getDataType(), this); - projectElementListeners.remove(listener.getDataType()); - } - } - } - - public void elementAdded(GameDataElement element, int index) { - if (projectElementListeners.get(element.getClass()) != null) { - for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { - l.elementAdded(element, index); - } - } - } - - public void elementRemoved(GameDataElement element, int index) { - if (projectElementListeners.get(element.getClass()) != null) { - for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { - l.elementRemoved(element, index); - } - } - } - - public void clearElementListeners() { - for (Class type : projectElementListeners.keySet()) { - target.getProject().removeElementListener(type, this); - } - } - - public Class getDataType() { - return null; - } - - -} +package com.gpl.rpg.atcontentstudio.ui; + +import com.gpl.rpg.atcontentstudio.ATContentStudio; +import com.gpl.rpg.atcontentstudio.Notification; +import com.gpl.rpg.atcontentstudio.model.GameDataElement; +import com.gpl.rpg.atcontentstudio.model.Project; +import com.gpl.rpg.atcontentstudio.model.ProjectElementListener; +import com.gpl.rpg.atcontentstudio.model.Workspace; +import com.gpl.rpg.atcontentstudio.model.gamedata.*; +import com.gpl.rpg.atcontentstudio.model.maps.TMXMap; +import com.gpl.rpg.atcontentstudio.utils.WeblateIntegration; +import com.jidesoft.swing.ComboBoxSearchable; +import com.jidesoft.swing.JideBoxLayout; + +import javax.swing.*; +import javax.swing.JSpinner.NumberEditor; +import javax.swing.event.*; +import javax.swing.text.DefaultFormatter; +import javax.swing.text.JTextComponent; +import java.awt.*; +import java.awt.event.*; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.regex.Matcher; + +public abstract class Editor extends JPanel implements ProjectElementListener { + + private static final long serialVersionUID = 241750514033596878L; + private static final FieldUpdateListener nullListener = new FieldUpdateListener() { + @Override + public void valueChanged(JComponent source, Object value) { + } + }; + + public static final String SAVE = "Save"; + public static final String DELETE = "Delete"; + public static final String REVERT = "Revert to original"; + public static final String ALTER = "Alter"; + public static final String GO_TO_ALTERED = "Go to altered"; + + + public static final String READ_ONLY_MESSAGE = + "" + + "This element is not modifiable.
" + + "Click on the \"Alter\" button to create a writable copy." + + "
"; + + public static final String ALTERED_EXISTS_MESSAGE = + "" + + "This element is not modifiable.
" + + "A writable copy exists in this project. Click on \"Go to altered\" to open it." + + "
"; + + public static final String ALTERED_MESSAGE = + "" + + "This element is a writable copy of an element of the referenced game source.
" + + "Take care not to break existing content when modifying it." + + "
"; + + public static final String CREATED_MESSAGE = + "" + + "This element is a creation of yours.
" + + "Do as you please." + + "
"; + + + public String name = "Editor"; + public Icon icon = null; + public GameDataElement target = null; + + public JLabel message = null; + + + public static JTextField addLabelField(JPanel pane, String label, String value) { + return addTextField(pane, label, value, false, nullListener); + } + + public static void addTranslationPane(JPanel pane, final JTextComponent tfComponent, final String initialValue) { + if (Workspace.activeWorkspace.settings.translatorLanguage.getCurrentValue() != null) { + JPanel labelPane = new JPanel(); + labelPane.setLayout(new JideBoxLayout(labelPane, JideBoxLayout.LINE_AXIS, 6)); + final JLabel translateLinkLabel = new JLabel(getWeblateLabelLink(initialValue)); + labelPane.add(translateLinkLabel, JideBoxLayout.FIX); + labelPane.add(new JLabel(" "), JideBoxLayout.FIX); + final JLabel translationStatus = new JLabel("Retrieving..."); + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusUnknownIcon())); + translationStatus.setToolTipText("Connecting to weblate..."); + labelPane.add(translationStatus, JideBoxLayout.VARY); + new Thread() { + public void run() { + WeblateIntegration.WeblateTranslationUnit unit = WeblateIntegration.getTranslationUnit(initialValue); + switch (unit.status) { + case absent: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon())); + translationStatus.setToolTipText("This string isn't managed by weblate (yet)."); + break; + case done: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusGreenIcon())); + translationStatus.setToolTipText("This string is translated on weblate."); + break; + case fuzzy: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusOrangeIcon())); + translationStatus.setToolTipText("This string is translated on weblate, but needs a review."); + break; + case notTranslated: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon())); + translationStatus.setToolTipText("This string isn't translated in your language on weblate yet."); + break; + case warning: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusOrangeIcon())); + translationStatus.setToolTipText("This string is translated on weblate, but triggered some weblate checks."); + break; + case error: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon())); + translationStatus.setToolTipText("Cannot connect to weblate. Check internet connection and firewall settings."); + break; + case notAllowed: + translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusBlueIcon())); + translationStatus.setToolTipText("You have not allowed ATCS to access to internet. You can change this in the workspace settings."); + break; + } + translationStatus.setText(unit.translatedText); + } + + }.start(); + pane.add(labelPane, JideBoxLayout.FIX); + tfComponent.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))); + translateLinkLabel.revalidate(); + translateLinkLabel.repaint(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))); + translateLinkLabel.revalidate(); + translateLinkLabel.repaint(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))); + translateLinkLabel.revalidate(); + translateLinkLabel.repaint(); + } + }); + translateLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + translateLinkLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + try { + Desktop.getDesktop().browse(new URI(WeblateIntegration.getWeblateLabelURI(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n"))))); + } catch (IOException e1) { + e1.printStackTrace(); + } catch (URISyntaxException e1) { + e1.printStackTrace(); + } + } + } + }); + } + } + + public static JTextField addTranslatableTextField(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { + final JTextField tfField = addTextField(pane, label, initialValue, editable, listener); + addTranslationPane(pane, tfField, initialValue); + return tfField; + } + + public static String getWeblateLabelLink(String text) { + return "Translate on weblate"; + } + + public static JTextField addTextField(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { + final JTextField tfField = new JTextField(initialValue); + addTextComponent(pane, label, editable, listener, tfField, false, false); + tfField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + listener.valueChanged(tfField, tfField.getText()); + } + }); + return tfField; + } + public static T addTextComponent(JPanel pane, String label, boolean editable, final FieldUpdateListener listener, T tfField, boolean specialNewLinesHandling, boolean scrollable) { + JPanel tfPane = new JPanel(); + tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6)); + JLabel tfLabel = new JLabel(label); + tfPane.add(tfLabel, JideBoxLayout.FIX); + tfField.setEditable(editable); + JComponent component; + if (scrollable){ + component = new JScrollPane(tfField); + }else{ + component = tfField; + } + tfPane.add(component, JideBoxLayout.VARY); + JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); + tfPane.add(nullify, JideBoxLayout.FIX); + nullify.setEnabled(editable); + pane.add(tfPane, JideBoxLayout.FIX); + + nullify.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tfField.setText(""); + listener.valueChanged(tfField, null); + } + }); + tfField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + String text = tfField.getText(); + if(specialNewLinesHandling) text = text.replaceAll("\n", Matcher.quoteReplacement("\n")); + listener.valueChanged(tfField, text); + } + + @Override + public void insertUpdate(DocumentEvent e) { + String text = tfField.getText(); + if(specialNewLinesHandling) text = text.replaceAll("\n", Matcher.quoteReplacement("\n")); + listener.valueChanged(tfField, text); + } + + @Override + public void changedUpdate(DocumentEvent e) { + String text = tfField.getText(); + if(specialNewLinesHandling) text = text.replaceAll("\n", Matcher.quoteReplacement("\n")); + listener.valueChanged(tfField, text); + } + }); + return tfField; + } + + + public static JTextArea addTranslatableTextArea(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { + final JTextArea tfArea = addTextArea(pane, label, initialValue, editable, listener); + addTranslationPane(pane, tfArea, initialValue); + return tfArea; + } + + public static JTextArea addTextArea(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) { + String text = initialValue == null ? "" : initialValue.replaceAll("\\n", "\n"); + final JTextArea tfArea = new JTextArea(text); + tfArea.setRows(2); + tfArea.setLineWrap(true); + tfArea.setWrapStyleWord(true); + + addTextComponent(pane, label, editable, listener, tfArea, true, true); + return tfArea; + } + +// public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, boolean allowNegatives, boolean editable) { +// return addIntegerField(pane, label, initialValue, allowNegatives, editable, nullListener); +// } + + public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, boolean allowNegatives, boolean editable, final FieldUpdateListener listener) { + return addIntegerField(pane, label, initialValue, 0, allowNegatives, editable, listener); + } + + public static > JSpinner addNumberField(JPanel pane, String label, boolean editable, final FieldUpdateListener listener, T minimum, T maximum, Number stepSize, T value, T defaultValue) { + JPanel tfPane = new JPanel(); + tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6)); + JLabel tfLabel = new JLabel(label); + tfPane.add(tfLabel, JideBoxLayout.FIX); + if (!(((minimum == null) || (minimum.compareTo(value) <= 0)) && + ((maximum == null) || (maximum.compareTo(value) >= 0)))) { + System.err.printf("Value for number field outside of range: %s <= %s <= %s is false%n", minimum, value, maximum); + value = defaultValue; + } + final JSpinner spinner = new JSpinner(new SpinnerNumberModel(value, minimum, maximum, stepSize)); + ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); + spinner.setEnabled(editable); + ((DefaultFormatter) ((NumberEditor) spinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true); + tfPane.add(spinner, JideBoxLayout.VARY); + JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); + tfPane.add(nullify, JideBoxLayout.FIX); + nullify.setEnabled(editable); + pane.add(tfPane, JideBoxLayout.FIX); + spinner.addChangeListener(e -> listener.valueChanged(spinner, spinner.getValue())); + nullify.addActionListener(e -> { + spinner.setValue(0); + listener.valueChanged(spinner, null); + }); + return spinner; + } + public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, Integer defaultValue, boolean allowNegatives, boolean editable, final FieldUpdateListener listener) { + int value = initialValue != null ? initialValue : defaultValue; + int minimum = allowNegatives ? Integer.MIN_VALUE : 0; + int maximum = Integer.MAX_VALUE; + return addNumberField(pane, label, editable, listener, minimum, maximum, 1, value, defaultValue); + } + + + private static final String percent = "%"; + private static final String ratio = "x/y"; + + public static JComponent addChanceField(JPanel pane, String label, String initialValue, String defaultValue, boolean editable, final FieldUpdateListener listener) { + int defaultChance = 1; + int defaultMaxChance = 100; + if (defaultValue != null) { + if (defaultValue.contains("/")) { + int c = defaultValue.indexOf('/'); + try { + defaultChance = Integer.parseInt(defaultValue.substring(0, c)); + } catch (NumberFormatException nfe) { + } + try { + defaultMaxChance = Integer.parseInt(defaultValue.substring(c + 1)); + } catch (NumberFormatException nfe) { + } + } else { + try { + defaultChance = Integer.parseInt(defaultValue); + } catch (NumberFormatException nfe) { + } + } + } + + boolean currentFormIsRatio = true; + int chance = defaultChance; + int maxChance = defaultMaxChance; + if (initialValue != null) { + if (initialValue.contains("/")) { + int c = initialValue.indexOf('/'); + try { + chance = Integer.parseInt(initialValue.substring(0, c)); + } catch (NumberFormatException nfe) { + } + try { + maxChance = Integer.parseInt(initialValue.substring(c + 1)); + } catch (NumberFormatException nfe) { + } + } else { + try { + chance = Integer.parseInt(initialValue); + currentFormIsRatio = false; + } catch (NumberFormatException nfe) { + } + } + } + + final JPanel tfPane = new JPanel(); + tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6)); + JLabel tfLabel = new JLabel(label); + tfPane.add(tfLabel, JideBoxLayout.FIX); + + final JComboBox entryTypeBox = new JComboBox(new String[]{percent, ratio}); + if (currentFormIsRatio) { + entryTypeBox.setSelectedItem(ratio); + } else { + entryTypeBox.setSelectedItem(percent); + } + entryTypeBox.setEnabled(editable); + tfPane.add(entryTypeBox, JideBoxLayout.FIX); + /////////////////////////////////////////////////////////////////////////////////////////////////// make sure "chance" is between 1 and 100. If lower than 1 get 1. If higher than 100, get chance/maxChance * 100... Then do the same with defaultChance, in case no value exist. + final SpinnerNumberModel percentModel = new SpinnerNumberModel(initialValue != null ? ((chance > 1 ? chance : 1) < 100 ? chance : (chance * 100 / maxChance)) : ((defaultChance > 1 ? defaultChance : 1) < 100 ? defaultChance : (defaultChance * 100 / defaultMaxChance)), 1, 100, 1); + final SpinnerNumberModel ratioChanceModel = new SpinnerNumberModel(initialValue != null ? chance : defaultChance, 1, Integer.MAX_VALUE, 1); + + final JSpinner chanceSpinner = new JSpinner(currentFormIsRatio ? ratioChanceModel : percentModel); + if (!currentFormIsRatio) + ((JSpinner.DefaultEditor) chanceSpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); + chanceSpinner.setEnabled(editable); + ((DefaultFormatter) ((NumberEditor) chanceSpinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true); + tfPane.add(chanceSpinner, JideBoxLayout.FLEXIBLE); + + final JLabel ratioLabel = new JLabel("/"); + tfPane.add(ratioLabel, JideBoxLayout.FIX); + + final JSpinner maxChanceSpinner = new JSpinner(new SpinnerNumberModel(initialValue != null ? maxChance : defaultMaxChance, 1, Integer.MAX_VALUE, 1)); + ((JSpinner.DefaultEditor) maxChanceSpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); + maxChanceSpinner.setEnabled(editable); + ((DefaultFormatter) ((NumberEditor) maxChanceSpinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true); + tfPane.add(maxChanceSpinner, JideBoxLayout.FLEXIBLE); + + if (!currentFormIsRatio) { + ratioLabel.setVisible(false); + maxChanceSpinner.setVisible(false); + tfPane.revalidate(); + tfPane.repaint(); + } + + final JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); + tfPane.add(nullify, JideBoxLayout.FIX); + nullify.setEnabled(editable); + pane.add(tfPane, JideBoxLayout.FIX); + + entryTypeBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (entryTypeBox.getSelectedItem() == percent) { + int chance = ((Integer) chanceSpinner.getValue()); + int maxChance = ((Integer) maxChanceSpinner.getValue()); + chance *= 100; + chance /= maxChance; + chance = Math.max(0, Math.min(100, chance)); + chanceSpinner.setModel(percentModel); + chanceSpinner.setValue(chance); + ((JSpinner.DefaultEditor) chanceSpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT); + ratioLabel.setVisible(false); + maxChanceSpinner.setVisible(false); + tfPane.revalidate(); + tfPane.repaint(); + listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString()); + } else if (entryTypeBox.getSelectedItem() == ratio) { + int chance = ((Integer) chanceSpinner.getValue()); + chanceSpinner.setModel(ratioChanceModel); + chanceSpinner.setValue(chance); + maxChanceSpinner.setValue(100); + ratioLabel.setVisible(true); + maxChanceSpinner.setVisible(true); + tfPane.revalidate(); + tfPane.repaint(); + listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString() + "/" + maxChanceSpinner.getValue().toString()); + } + } + }); + chanceSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (entryTypeBox.getSelectedItem() == percent) { + listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString()); + } else if (entryTypeBox.getSelectedItem() == ratio) { + listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString() + "/" + maxChanceSpinner.getValue().toString()); + } + } + }); + maxChanceSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + listener.valueChanged(chanceSpinner, chanceSpinner.getValue().toString() + "/" + maxChanceSpinner.getValue().toString()); + } + }); + nullify.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + chanceSpinner.setValue(1); + listener.valueChanged(chanceSpinner, null); + } + }); + return chanceSpinner; + } + +// public static JSpinner addDoubleField(JPanel pane, String label, Double initialValue, boolean editable) { +// return addDoubleField(pane, label, initialValue, editable, nullListener); +// } + + public static JSpinner addDoubleField(JPanel pane, String label, Double initialValue, boolean editable, final FieldUpdateListener listener) { + double minimum = 0.0d; + double defaultValue = 0.0d; + double value = initialValue != null ? initialValue : minimum; + double maximum = Float.valueOf(Float.MAX_VALUE).doubleValue(); + return addNumberField(pane, label, editable, listener, minimum, maximum, 1.0d, value, defaultValue); + } + + public static IntegerBasedCheckBox addIntegerBasedCheckBox(JPanel pane, String label, Integer initialValue, boolean editable) { + return addIntegerBasedCheckBox(pane, label, initialValue, editable, nullListener); + } + + public static IntegerBasedCheckBox addIntegerBasedCheckBox(JPanel pane, String label, Integer initialValue, boolean editable, final FieldUpdateListener listener) { + JPanel ibcbPane = new JPanel(); + ibcbPane.setLayout(new BorderLayout()); + final IntegerBasedCheckBox ibcb = new IntegerBasedCheckBox(); + ibcb.setText(label); + ibcb.setIntegerValue(initialValue); + ibcb.setEnabled(editable); + ibcbPane.add(ibcb, BorderLayout.WEST); + ibcbPane.add(new JPanel(), BorderLayout.CENTER); + pane.add(ibcbPane, JideBoxLayout.FIX); + ibcb.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + listener.valueChanged(ibcb, ibcb.getIntegerValue()); + } + }); + return ibcb; + } + + public static BooleanBasedCheckBox addBooleanBasedCheckBox(JPanel pane, String label, Boolean initialValue, boolean editable, final FieldUpdateListener listener) { + JPanel bbcbPane = new JPanel(); + bbcbPane.setLayout(new BorderLayout()); + final BooleanBasedCheckBox bbcb = new BooleanBasedCheckBox(); + bbcb.setText(label); + bbcb.setBooleanValue(initialValue); + bbcb.setEnabled(editable); + bbcbPane.add(bbcb, BorderLayout.WEST); + bbcbPane.add(new JPanel(), BorderLayout.CENTER); + pane.add(bbcbPane, JideBoxLayout.FIX); + bbcb.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + listener.valueChanged(bbcb, bbcb.isSelected()); + } + }); + return bbcb; + } + + @SuppressWarnings("rawtypes") + public static JComboBox addEnumValueBox(JPanel pane, String label, Enum[] values, Enum initialValue, boolean writable) { + return addEnumValueBox(pane, label, values, initialValue, writable, new FieldUpdateListener() { + @Override + public void valueChanged(JComponent source, Object value) { + } + }); + } + + @SuppressWarnings("rawtypes") + public static JComboBox addEnumValueBox(JPanel pane, String label, Enum[] values, Enum initialValue, boolean writable, final FieldUpdateListener listener) { + JPanel comboPane = new JPanel(); + comboPane.setLayout(new JideBoxLayout(comboPane, JideBoxLayout.LINE_AXIS, 6)); + JLabel comboLabel = new JLabel(label); + comboPane.add(comboLabel, JideBoxLayout.FIX); + @SuppressWarnings("unchecked") final JComboBox enumValuesCombo = new JComboBox(values); + enumValuesCombo.setEnabled(writable); + enumValuesCombo.setSelectedItem(initialValue); + comboPane.add(enumValuesCombo, JideBoxLayout.VARY); + enumValuesCombo.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + listener.valueChanged(enumValuesCombo, e.getItem()); + } + } + }); + JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); + comboPane.add(nullify, JideBoxLayout.FIX); + nullify.setEnabled(writable); + nullify.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + enumValuesCombo.setSelectedItem(null); + listener.valueChanged(enumValuesCombo, null); + } + }); + + pane.add(comboPane, JideBoxLayout.FIX); + return enumValuesCombo; + } + + + public MyComboBox addNPCBox(JPanel pane, Project proj, String label, NPC npc, boolean writable, FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, npc) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public NPC getTypedElementAt(int index) { + return project.getNPC(index); + } + + @Override + public int getSize() { + return project.getNPCCount() + 1; + } + }; + return addGDEBox(pane, label, npc, NPC.class, comboModel, writable, listener); + } + + public MyComboBox addActorConditionBox(JPanel pane, Project proj, String label, ActorCondition acond, boolean writable, FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, acond) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public ActorCondition getTypedElementAt(int index) { + return project.getActorCondition(index); + } + + @Override + public int getSize() { + return project.getActorConditionCount() + 1; + } + }; + return addGDEBox(pane, label, acond, ActorCondition.class, comboModel, writable, listener); + } + + public MyComboBox addItemBox(JPanel pane, Project proj, String label, Item item, boolean writable, FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, item) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public Item getTypedElementAt(int index) { + return project.getItem(index); + } + + @Override + public int getSize() { + return project.getItemCount() + 1; + } + }; + return addGDEBox(pane, label, item, Item.class, comboModel, writable, listener); + } + + public MyComboBox addItemCategoryBox(JPanel pane, Project proj, String label, ItemCategory ic, boolean writable, FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, ic) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public ItemCategory getTypedElementAt(int index) { + return project.getItemCategory(index); + } + + @Override + public int getSize() { + return project.getItemCategoryCount() + 1; + } + }; + return addGDEBox(pane, label, ic, ItemCategory.class, comboModel, writable, listener); + } + + public MyComboBox addQuestBox(JPanel pane, Project proj, String label, Quest quest, boolean writable, FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, quest) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public Quest getTypedElementAt(int index) { + return project.getQuest(index); + } + + @Override + public int getSize() { + return project.getQuestCount() + 1; + } + }; + return addGDEBox(pane, label, quest, Quest.class, comboModel, writable, listener); + } + + public MyComboBox addDroplistBox(JPanel pane, Project proj, String label, Droplist droplist, boolean writable, FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, droplist) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public Droplist getTypedElementAt(int index) { + return project.getDroplist(index); + } + + @Override + public int getSize() { + return project.getDroplistCount() + 1; + } + }; + return addGDEBox(pane, label, droplist, Droplist.class, comboModel, writable, listener); + } + + public MyComboBox addDialogueBox(JPanel pane, Project proj, String label, Dialogue dialogue, boolean writable, final FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, dialogue) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public Dialogue getTypedElementAt(int index) { + return project.getDialogue(index); + } + + @Override + public int getSize() { + return project.getDialogueCount() + 1; + } + }; + return addGDEBox(pane, label, dialogue, Dialogue.class, comboModel, writable, listener); + } + + public MyComboBox addMapBox(JPanel pane, Project proj, String label, TMXMap map, boolean writable, final FieldUpdateListener listener) { + final GDEComboModel comboModel = new GDEComboModel(proj, map) { + private static final long serialVersionUID = 2638082961277241764L; + + @Override + public TMXMap getTypedElementAt(int index) { + return project.getMap(index); + } + + @Override + public int getSize() { + return project.getMapCount() + 1; + } + }; + return addGDEBox(pane, label, map, TMXMap.class, comboModel, writable, listener); + } + + @SuppressWarnings("unchecked") + public MyComboBox addGDEBox(JPanel pane, String label, GameDataElement gde, final Class dataClass, final GDEComboModel comboModel, final boolean writable, final FieldUpdateListener listener) { + JPanel gdePane = new JPanel(); + gdePane.setLayout(new JideBoxLayout(gdePane, JideBoxLayout.LINE_AXIS, 6)); + JLabel gdeLabel = new JLabel(label); + gdePane.add(gdeLabel, JideBoxLayout.FIX); + final MyComboBox gdeBox = new MyComboBox(dataClass, comboModel); + gdeBox.setRenderer(new GDERenderer(false, writable)); + new ComboBoxSearchable(gdeBox) { + @Override + protected String convertElementToString(Object object) { + if (object == null) return "none"; + else return ((GameDataElement) object).getDesc(); + } + }; + gdeBox.setEnabled(writable); + gdePane.add(gdeBox, JideBoxLayout.VARY); + final JButton goToGde = new JButton((Icon) ((gde != null) ? new ImageIcon(gde.getIcon()) : (writable ? new ImageIcon(DefaultIcons.getCreateIcon()) : null))); + goToGde.setEnabled(gde != null || writable); + goToGde.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + GameDataElement selected = ((GameDataElement) comboModel.getSelectedItem()); + if (selected != null) { + ATContentStudio.frame.openEditor(((GameDataElement) comboModel.getSelectedItem())); + ATContentStudio.frame.selectInTree((GameDataElement) comboModel.getSelectedItem()); + } else if (writable) { + JSONCreationWizard wizard = new JSONCreationWizard(((GameDataElement) target).getProject(), dataClass); + wizard.addCreationListener(new JSONCreationWizard.CreationCompletedListener() { + + @Override + public void elementCreated(JSONElement created) { + gdeBox.setSelectedItem(created); + } + }); + wizard.setVisible(true); + } + } + }); + gdePane.add(goToGde, JideBoxLayout.FIX); + gdeBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (gdeBox.getModel().getSelectedItem() == null) { + goToGde.setIcon((writable ? new ImageIcon(DefaultIcons.getCreateIcon()) : null)); + goToGde.setEnabled(writable); + } else { + goToGde.setIcon(new ImageIcon(((GameDataElement) comboModel.getSelectedItem()).getIcon())); + goToGde.setEnabled(true); + } + listener.valueChanged(gdeBox, gdeBox.getModel().getSelectedItem()); + } + }); + JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon())); + gdePane.add(nullify, JideBoxLayout.FIX); + nullify.setEnabled(writable); + nullify.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + gdeBox.setSelectedItem(null); + } + }); + pane.add(gdePane, JideBoxLayout.FIX); + + return gdeBox; + } + + public JComboBox addQuestStageBox(JPanel pane, Project proj, String label, Integer initialValue, boolean writable, final FieldUpdateListener listener, Quest quest, @SuppressWarnings("rawtypes") final JComboBox questSelectionBox) { + JPanel gdePane = new JPanel(); + gdePane.setLayout(new JideBoxLayout(gdePane, JideBoxLayout.LINE_AXIS, 6)); + JLabel gdeLabel = new JLabel(label); + gdePane.add(gdeLabel, JideBoxLayout.FIX); + + QuestStage initial = null; + if (quest != null) { + initial = quest.getStage(initialValue); + } + final QuestStageComboModel comboModel = new QuestStageComboModel(proj, initial, quest); + final JComboBox combo = new JComboBox(comboModel); + combo.setRenderer(new GDERenderer(false, writable)); + new ComboBoxSearchable(combo) { + @Override + protected String convertElementToString(Object object) { + if (object == null) return "none"; + else return ((GameDataElement) object).getDesc(); + } + }; + questSelectionBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (comboModel.selected != null) { + Editor.this.target.removeBacklink(comboModel.selected); + } + Quest newQuest = (Quest) questSelectionBox.getSelectedItem(); + comboModel.changeQuest(newQuest); + combo.revalidate(); + } + }); + combo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + listener.valueChanged(combo, comboModel.selected == null ? null : comboModel.selected.progress); + } + }); + + + combo.setEnabled(writable); + gdePane.add(combo, JideBoxLayout.VARY); + + pane.add(gdePane, JideBoxLayout.FIX); + + return combo; + } + + + @SuppressWarnings({"rawtypes"}) + public JList addBacklinksList(JPanel pane, GameDataElement gde) { + return addBacklinksList(pane, gde, "Elements linking to this one"); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public JList addBacklinksList(JPanel pane, GameDataElement gde, String title) { + final JList list = new JList(new GDEBacklinksListModel(gde)); + list.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + ATContentStudio.frame.openEditor((GameDataElement) list.getSelectedValue()); + ATContentStudio.frame.selectInTree((GameDataElement) list.getSelectedValue()); + } + } + }); + list.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + ATContentStudio.frame.openEditor((GameDataElement) list.getSelectedValue()); + ATContentStudio.frame.selectInTree((GameDataElement) list.getSelectedValue()); + } + } + }); + list.setCellRenderer(new GDERenderer(true, false)); + CollapsiblePanel colPane = new CollapsiblePanel(title); + colPane.setLayout(new JideBoxLayout(colPane, JideBoxLayout.PAGE_AXIS)); + colPane.add(new JScrollPane(list), JideBoxLayout.FIX); + colPane.add(new JPanel(), JideBoxLayout.FIX); + if (gde.getBacklinks() == null || gde.getBacklinks().isEmpty()) { + colPane.collapse(); + } + pane.add(colPane, JideBoxLayout.FIX); + return list; + } + + public static abstract class GDEComboModel extends AbstractListModel implements ComboBoxModel { + + private static final long serialVersionUID = -5854574666510314715L; + + public Project project; + public E selected; + + public GDEComboModel(Project proj, E initial) { + this.project = proj; + this.selected = initial; + } + + @Override + public abstract int getSize(); + + @Override + public E getElementAt(int index) { + if (index == 0) { + return null; + } + return getTypedElementAt(index - 1); + } + + public abstract E getTypedElementAt(int index); + + @SuppressWarnings("unchecked") + @Override + public void setSelectedItem(Object anItem) { + selected = (E) anItem; + } + + @Override + public Object getSelectedItem() { + return selected; + } + + public void itemAdded(E item, int index) { + fireIntervalAdded(this, index, index); + } + + public void itemRemoved(E item, int index) { + fireIntervalRemoved(this, index, index); + } + + } + + public static class GDERenderer extends DefaultListCellRenderer { + + private static final long serialVersionUID = 6819681566800482793L; + + private boolean includeType; + private boolean writable; + + public GDERenderer(boolean includeType, boolean writable) { + super(); + this.includeType = includeType; + this.writable = writable; + } + + @SuppressWarnings("rawtypes") + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value == null) { + label.setText("None" + (writable ? ". Click on the button to create one." : "")); + } else { + if (includeType && ((GameDataElement) value).getDataType() != null) { + if (value instanceof QuestStage) { + String text = ((GameDataElement) value).getDesc(); + if (text.length() > 60) { + text = text.substring(0, 57) + "..."; + } + label.setText(((GameDataElement) value).getDataType().toString() + "/" + ((Quest) ((QuestStage) value).parent).id + "#" + ((QuestStage) value).progress + ":" + text); + } else { + label.setText(((GameDataElement) value).getDataType().toString() + "/" + ((GameDataElement) value).getDesc()); + } + } else { + if (value instanceof QuestStage) { + String text = ((GameDataElement) value).getDesc(); + if (text.length() > 60) { + text = text.substring(0, 57) + "..."; + } + label.setText(text); + } else { + label.setText(((GameDataElement) value).getDesc()); + } + } + if (((GameDataElement) value).getIcon() == null) { + Notification.addError("Unable to find icon for " + ((GameDataElement) value).getDesc()); + } else { + label.setIcon(new ImageIcon(((GameDataElement) value).getIcon())); + } + } + return label; + } + + } + + public static class QuestStageComboModel extends AbstractListModel implements ComboBoxModel { + + private static final long serialVersionUID = -5854574666510314715L; + + public Project project; + public Quest currentQuest; + public QuestStage selected; + + public QuestStageComboModel(Project proj, QuestStage initial, Quest quest) { + this.project = proj; + this.currentQuest = quest; + this.selected = initial; + } + + @Override + public int getSize() { + if (currentQuest == null) return 1; + return currentQuest.stages.size() + 1; + } + + @Override + public QuestStage getElementAt(int index) { + if (index == 0) { + return null; + } + return currentQuest.stages.get(index - 1); + } + + @Override + public void setSelectedItem(Object anItem) { + selected = (QuestStage) anItem; + } + + @Override + public Object getSelectedItem() { + return selected; + } + + public void itemAdded(QuestStage item, int index) { + fireIntervalAdded(this, index, index); + } + + public void itemRemoved(QuestStage item, int index) { + fireIntervalRemoved(this, index, index); + } + + public void changeQuest(Quest newQuest) { + int size = getSize(); + currentQuest = null; + selected = null; + fireIntervalRemoved(this, 1, size); + currentQuest = newQuest; + fireIntervalAdded(this, 1, getSize()); + } + + } + + + public static class GDEBacklinksListModel implements ListenerCollectionModel { + + GameDataElement source; + + public GDEBacklinksListModel(GameDataElement source) { + super(); + this.source = source; + source.addBacklinkListener(new GameDataElement.BacklinksListener() { + @Override + public void backlinkRemoved(GameDataElement gde) { + fireListChanged(); + } + + @Override + public void backlinkAdded(GameDataElement gde) { + fireListChanged(); + } + }); + } + + @Override + public Collection getElements() { + return source.getBacklinks(); + } + + List listeners = new CopyOnWriteArrayList(); + + @Override + public List getListeners() { + return listeners; + } + + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public class MyComboBox extends JComboBox implements ProjectElementListener { + + private static final long serialVersionUID = -4184228604170642567L; + + Class dataType; + + public MyComboBox(Class dataType, ComboBoxModel model) { + super(model); + this.dataType = dataType; + Editor.this.addElementListener(dataType, this); + } + + @Override + public void elementAdded(GameDataElement added, int index) { + ((GDEComboModel) getModel()).itemAdded(added, index); + } + + @Override + public void elementRemoved(GameDataElement removed, int index) { + ((GDEComboModel) getModel()).itemRemoved(removed, index); + } + + @Override + public Class getDataType() { + return dataType; + } + + } + + public abstract void targetUpdated(); + + + transient Map, List> projectElementListeners = new HashMap, List>(); + + public void addElementListener(Class interestingType, ProjectElementListener listener) { + if (projectElementListeners.get(interestingType) == null) { + projectElementListeners.put(interestingType, new ArrayList()); + target.getProject().addElementListener(interestingType, this); + } + projectElementListeners.get(interestingType).add(listener); + } + + public void removeElementListener(ProjectElementListener listener) { + if (listener == null) return; + if (projectElementListeners.get(listener.getDataType()) != null) { + projectElementListeners.get(listener.getDataType()).remove(listener); + if (projectElementListeners.get(listener.getDataType()).isEmpty()) { + target.getProject().removeElementListener(listener.getDataType(), this); + projectElementListeners.remove(listener.getDataType()); + } + } + } + + public void elementAdded(GameDataElement element, int index) { + if (projectElementListeners.get(element.getClass()) != null) { + for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { + l.elementAdded(element, index); + } + } + } + + public void elementRemoved(GameDataElement element, int index) { + if (projectElementListeners.get(element.getClass()) != null) { + for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { + l.elementRemoved(element, index); + } + } + } + + public void clearElementListeners() { + for (Class type : projectElementListeners.keySet()) { + target.getProject().removeElementListener(type, this); + } + } + + public Class getDataType() { + return null; + } + + +}