X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fui%2FConfigItem.java;h=3ae029e8484bb0bc2469cb7e1f1086d530a6c6d9;hb=d80040bdb4bacb152c67eb40141f9e1dc1aa3c8b;hp=045d83870213914a806e85609a6e60c9a05ba4de;hpb=249e4ef4c3c768c02287504ca51cda54cccf40f7;p=nikiroo-utils.git diff --git a/src/be/nikiroo/utils/ui/ConfigItem.java b/src/be/nikiroo/utils/ui/ConfigItem.java index 045d838..3ae029e 100644 --- a/src/be/nikiroo/utils/ui/ConfigItem.java +++ b/src/be/nikiroo/utils/ui/ConfigItem.java @@ -9,7 +9,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import javax.swing.BoxLayout; @@ -40,13 +39,13 @@ import be.nikiroo.utils.resources.MetaInfo; * @param * the type of {@link Bundle} to edit */ -public class ConfigItem> extends JPanel { +public abstract class ConfigItem> extends JPanel { private static final long serialVersionUID = 1L; private static int minimumHeight = -1; /** A small 16x16 "?" blue in PNG, base64 encoded. */ - private static String img64Info = // + private static String img64info = // "" + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI" + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wURFRg6IrtcdgAAATdJREFUOMvtkj8sQ1EUxr9z/71G" @@ -58,7 +57,7 @@ public class ConfigItem> extends JPanel { + "eUvgn195AwlZWyvjtQdhAAAAAElFTkSuQmCC"; /** A small 16x16 "+" image with colours */ - private static String img54add = // + private static String img64add = // "" + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI" + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUeES0QBFvvnAAAAB1pVFh0Q29tbWVudAAAAAAAQ3Jl" @@ -75,7 +74,7 @@ public class ConfigItem> extends JPanel { + "oRFoAAAAAElFTkSuQmCC"; /** A small 32x32 "-" image with colours */ - private static String img64Remove = // + private static String img64remove = // "" + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI" + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUeESw5X/JGsQAAAB1pVFh0Q29tbWVudAAAAAAAQ3Jl" @@ -91,89 +90,118 @@ public class ConfigItem> extends JPanel { + "zhT8sfdKeehWkUQAeJ7WcH23xTz1uPBwf1hclA3mBZjPojFOIOSsVPpmN1OznfpA+Gn+2kCHqg/d" + "LhIA/AFU5d0V6gTjtQAAAABJRU5ErkJggg=="; - /** The original value before current changes. */ - private Object orig; - private List dirtyBits; + /** The code base */ + private final ConfigItemBase base; - private JComponent field; - private List fields = new ArrayList(); + /** The main panel with all the fields in it. */ + private JPanel main; - /** The {@link MetaInfo} linked to the field. */ - protected MetaInfo info; + /** + * Prepare a new {@link ConfigItem} instance, linked to the given + * {@link MetaInfo}. + * + * @param info + * the info + * @param autoDirtyHandling + * TRUE to automatically manage the setDirty/Save operations, + * FALSE if you want to do it yourself via + * {@link ConfigItem#setDirtyItem(int)} + */ + protected ConfigItem(MetaInfo info, boolean autoDirtyHandling) { + base = new ConfigItemBase(info, autoDirtyHandling) { + @Override + protected JComponent createEmptyField(int item) { + return ConfigItem.this.createEmptyField(item); + } + + @Override + protected Object getFromInfo(int item) { + return ConfigItem.this.getFromInfo(item); + } + + @Override + protected void setToInfo(Object value, int item) { + ConfigItem.this.setToInfo(value, item); + } + + @Override + protected Object getFromField(int item) { + return ConfigItem.this.getFromField(item); + } + + @Override + protected void setToField(Object value, int item) { + ConfigItem.this.setToField(value, item); + } + + @Override + public JComponent createField(int item) { + JComponent field = super.createField(item); + + int height = Math.max(getMinimumHeight(), + field.getMinimumSize().height); + field.setPreferredSize(new Dimension(200, height)); + + return field; + } + + @Override + public List reload() { + List removed = base.reload(); + if (!removed.isEmpty()) { + for (JComponent c : removed) { + main.remove(c); + } + main.revalidate(); + main.repaint(); + } + + return removed; + } + + @Override + protected JComponent removeItem(int item) { + JComponent removed = super.removeItem(item); + main.remove(removed); + main.revalidate(); + main.repaint(); + + return removed; + } + }; + } /** * Create a new {@link ConfigItem} for the given {@link MetaInfo}. * - * @param info - * the {@link MetaInfo} * @param nhgap * negative horisontal gap in pixel to use for the label, i.e., * the step lock sized labels will start smaller by that amount * (the use case would be to align controls that start at a * different horisontal position) */ - public ConfigItem(MetaInfo info, int nhgap) { - this(info, true); - - ConfigItem configItem; - switch (info.getFormat()) { - case BOOLEAN: - configItem = new ConfigItemBoolean(info); - break; - case COLOR: - configItem = new ConfigItemColor(info); - break; - case FILE: - configItem = new ConfigItemBrowse(info, false); - break; - case DIRECTORY: - configItem = new ConfigItemBrowse(info, true); - break; - case COMBO_LIST: - configItem = new ConfigItemCombobox(info, true); - break; - case FIXED_LIST: - configItem = new ConfigItemCombobox(info, false); - break; - case INT: - configItem = new ConfigItemInteger(info); - break; - case PASSWORD: - configItem = new ConfigItemPassword(info); - break; - case LOCALE: - configItem = new ConfigItemLocale(info); - break; - case STRING: - default: - configItem = new ConfigItemString(info); - break; - } - - if (info.isArray()) { + public void init(int nhgap) { + if (getInfo().isArray()) { this.setLayout(new BorderLayout()); add(label(nhgap), BorderLayout.WEST); - final JPanel main = new JPanel(); + main = new JPanel(); + main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); - int size = info.getListSize(false); + int size = getInfo().getListSize(false); for (int i = 0; i < size; i++) { - JComponent field = configItem.createComponent(i); - main.add(field); + addItemWithMinusPanel(i); } + main.revalidate(); + main.repaint(); final JButton add = new JButton(); - setImage(add, img54add, "+"); + setImage(add, img64add, "+"); - final ConfigItem fconfigItem = configItem; add.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - JComponent field = fconfigItem - .createComponent(fconfigItem.info - .getListSize(false)); - main.add(field); - + addItemWithMinusPanel(base.getFieldsSize()); main.revalidate(); main.repaint(); } @@ -191,35 +219,19 @@ public class ConfigItem> extends JPanel { this.setLayout(new BorderLayout()); add(label(nhgap), BorderLayout.WEST); - JComponent field = configItem.createComponent(-1); + JComponent field = base.createField(-1); add(field, BorderLayout.CENTER); } } - /** - * Prepare a new {@link ConfigItem} instance, linked to the given - * {@link MetaInfo}. - * - * @param info - * the info - * @param autoDirtyHandling - * TRUE to automatically manage the setDirty/Save operations, - * FALSE if you want to do it yourself via - * {@link ConfigItem#setDirtyItem(int)} - */ - protected ConfigItem(MetaInfo info, boolean autoDirtyHandling) { - this.info = info; - if (!autoDirtyHandling) { - dirtyBits = new ArrayList(); - } + /** The {@link MetaInfo} linked to the field. */ + public MetaInfo getInfo() { + return base.getInfo(); } /** - * Create an empty graphical component to be used later by - * {@link ConfigItem#getField(int)}. - *

- * Note that {@link ConfigItem#reload(int)} will be called after it was - * created. + * Retrieve the associated graphical component that was created with + * {@link ConfigItemBase#createEmptyField(int)}. * * @param item * the item number to get for an array of values, or -1 to get @@ -228,98 +240,79 @@ public class ConfigItem> extends JPanel { * * @return the graphical component */ - protected JComponent createField(@SuppressWarnings("unused") int item) { - // Not used by the main class, only the sublasses - return null; + protected JComponent getField(int item) { + return base.getField(item); } /** - * Get the information from the {@link MetaInfo} in the subclass preferred - * format. + * Manually specify that the given item is "dirty" and thus should be saved + * when asked. + *

+ * Has no effect if the class is using automatic dirty handling (see + * {@link ConfigItemBase#ConfigItem(MetaInfo, boolean)}). * * @param item * the item number to get for an array of values, or -1 to get * the whole value (has no effect if {@link MetaInfo#isArray()} * is FALSE) - * - * @return the information in the subclass preferred format */ - protected Object getFromInfo(@SuppressWarnings("unused") int item) { - // Not used by the main class, only the subclasses - return null; + protected void setDirtyItem(int item) { + base.setDirtyItem(item); } /** - * Set the value to the {@link MetaInfo}. + * Check if the value changed since the last load/save into the linked + * {@link MetaInfo}. + *

+ * Note that we consider NULL and an Empty {@link String} to be equals. * * @param value - * the value in the subclass preferred format - * @param item - * the item number to get for an array of values, or -1 to get - * the whole value (has no effect if {@link MetaInfo#isArray()} - * is FALSE) - */ - protected void setToInfo(@SuppressWarnings("unused") Object value, - @SuppressWarnings("unused") int item) { - // Not used by the main class, only the subclasses - } - - /** + * the value to test * @param item * the item number to get for an array of values, or -1 to get * the whole value (has no effect if {@link MetaInfo#isArray()} * is FALSE) * - * @return + * @return TRUE if it has */ - protected Object getFromField(@SuppressWarnings("unused") int item) { - // Not used by the main class, only the subclasses - return null; + protected boolean hasValueChanged(Object value, int item) { + return base.hasValueChanged(value, item); } - /** - * Set the value (in the subclass preferred format) into the field. - * - * @param value - * the value in the subclass preferred format - * @param item - * the item number to get for an array of values, or -1 to get - * the whole value (has no effect if {@link MetaInfo#isArray()} - * is FALSE) - */ - protected void setToField(@SuppressWarnings("unused") Object value, - @SuppressWarnings("unused") int item) { - // Not used by the main class, only the subclasses + private void addItemWithMinusPanel(int item) { + JPanel minusPanel = createMinusPanel(item); + JComponent field = base.addItem(item, minusPanel); + minusPanel.add(field, BorderLayout.CENTER); } - /** - * Create a new field for the given graphical component at the given index - * (note that the component is usually created by - * {@link ConfigItem#createField(int)}). - * - * @param item - * the item number to get for an array of values, or -1 to get - * the whole value (has no effect if {@link MetaInfo#isArray()} - * is FALSE) - * @param field - * the graphical component - */ - private void setField(int item, JComponent field) { - if (item < 0) { - this.field = field; - return; - } + private JPanel createMinusPanel(final int item) { + JPanel minusPanel = new JPanel(new BorderLayout()); - for (int i = fields.size(); i <= item; i++) { - fields.add(null); - } + final JButton remove = new JButton(); + setImage(remove, img64remove, "-"); - fields.set(item, field); + remove.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + base.removeItem(item); + } + }); + + minusPanel.add(remove, BorderLayout.EAST); + + main.add(minusPanel); + main.revalidate(); + main.repaint(); + + return minusPanel; } /** - * Retrieve the associated graphical component that was created with + * Create an empty graphical component to be used later by * {@link ConfigItem#createField(int)}. + *

+ * Note that {@link ConfigItem#reload(int)} will be called after it was + * created by {@link ConfigItem#createField(int)}. * * @param item * the item number to get for an array of values, or -1 to get @@ -328,130 +321,58 @@ public class ConfigItem> extends JPanel { * * @return the graphical component */ - protected JComponent getField(int item) { - if (item < 0) { - return field; - } - - if (item < fields.size()) { - return fields.get(item); - } - - return null; - } + abstract protected JComponent createEmptyField(int item); /** - * Manually specify that the given item is "dirty" and thus should be saved - * when asked. - *

- * Has no effect if the class is using automatic dirty handling (see - * {@link ConfigItem#ConfigItem(MetaInfo, boolean)}). + * Get the information from the {@link MetaInfo} in the subclass preferred + * format. * * @param item * the item number to get for an array of values, or -1 to get * the whole value (has no effect if {@link MetaInfo#isArray()} * is FALSE) - */ - protected void setDirtyItem(int item) { - if (dirtyBits != null) { - dirtyBits.add(item); - } - } - - /** - * Check if the value changed since the last load/save into the linked - * {@link MetaInfo}. - *

- * Note that we consider NULL and an Empty {@link String} to be equals. * - * @param value - * the value to test - * - * @return TRUE if it has + * @return the information in the subclass preferred format */ - protected boolean hasValueChanged(Object value) { - // We consider "" and NULL to be equals - return !orig.equals(value == null ? "" : value); - } + abstract protected Object getFromInfo(int item); /** - * Reload the values to what they currently are in the {@link MetaInfo}. + * Set the value to the {@link MetaInfo}. * + * @param value + * the value in the subclass preferred format * @param item * the item number to get for an array of values, or -1 to get * the whole value (has no effect if {@link MetaInfo#isArray()} * is FALSE) */ - protected void reload(int item) { - Object value = getFromInfo(item); - setToField(value, item); - - // We consider "" and NULL to be equals - orig = (value == null ? "" : value); - } + abstract protected void setToInfo(Object value, int item); /** - * If the item has been modified, set the {@link MetaInfo} to dirty then - * modify it to, reflect the changes so it can be saved later. - *

- * This method does not call {@link MetaInfo#save(boolean)}. + * The value present in the given item's related field in the subclass + * preferred format. * * @param item * the item number to get for an array of values, or -1 to get * the whole value (has no effect if {@link MetaInfo#isArray()} * is FALSE) + * + * @return the value present in the given item's related field in the + * subclass preferred format */ - protected void save(int item) { - Object value = getFromField(item); - - boolean dirty = false; - if (dirtyBits != null) { - dirty = dirtyBits.remove((Integer) item); - } else { - // We consider "" and NULL to be equals - dirty = hasValueChanged(value); - } - - if (dirty) { - info.setDirty(); - setToInfo(value, item); - orig = (value == null ? "" : value); - } - } + abstract protected Object getFromField(int item); /** + * Set the value (in the subclass preferred format) into the field. * + * @param value + * the value in the subclass preferred format * @param item * the item number to get for an array of values, or -1 to get * the whole value (has no effect if {@link MetaInfo#isArray()} * is FALSE) - * @param addTo - * @param nhgap - * - * @return */ - protected JComponent createComponent(final int item) { - setField(item, createField(item)); - reload(item); - - info.addReloadedListener(new Runnable() { - @Override - public void run() { - reload(item); - } - }); - info.addSaveListener(new Runnable() { - @Override - public void run() { - save(item); - } - }); - - JComponent field = getField(item); - setPreferredSize(field); - - return field; - } + abstract protected void setToField(Object value, int item); /** * Create a label which width is constrained in lock steps. @@ -465,7 +386,7 @@ public class ConfigItem> extends JPanel { * @return the label */ protected JComponent label(int nhgap) { - final JLabel label = new JLabel(info.getName()); + final JLabel label = new JLabel(getInfo().getName()); Dimension ps = label.getPreferredSize(); if (ps == null) { @@ -487,7 +408,7 @@ public class ConfigItem> extends JPanel { @Override public void run() { StringBuilder builder = new StringBuilder(); - String text = (info.getDescription().replace("\\n", "\n")) + String text = (getInfo().getDescription().replace("\\n", "\n")) .trim(); for (String line : StringUtils.justifyText(text, 80, Alignment.LEFT)) { @@ -497,14 +418,14 @@ public class ConfigItem> extends JPanel { builder.append(line); } text = builder.toString(); - JOptionPane.showMessageDialog(ConfigItem.this, text, - info.getName(), JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(ConfigItem.this, text, getInfo() + .getName(), JOptionPane.INFORMATION_MESSAGE); } }; JLabel help = new JLabel(""); help.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - setImage(help, img64Info, "?"); + setImage(help, img64info, "?"); help.addMouseListener(new MouseAdapter() { @Override @@ -531,18 +452,62 @@ public class ConfigItem> extends JPanel { return pane; } - protected void setPreferredSize(JComponent field) { - int height = Math - .max(getMinimumHeight(), field.getMinimumSize().height); - setPreferredSize(new Dimension(200, height)); - } + /** + * Create a new {@link ConfigItem} for the given {@link MetaInfo}. + * + * @param + * the type of {@link Bundle} to edit + * + * @param info + * the {@link MetaInfo} + * @param nhgap + * negative horisontal gap in pixel to use for the label, i.e., + * the step lock sized labels will start smaller by that amount + * (the use case would be to align controls that start at a + * different horisontal position) + * + * @return the new {@link ConfigItem} + */ + static public > ConfigItem createItem( + MetaInfo info, int nhgap) { - static private int getMinimumHeight() { - if (minimumHeight < 0) { - minimumHeight = new JTextField("Test").getMinimumSize().height; + ConfigItem configItem; + switch (info.getFormat()) { + case BOOLEAN: + configItem = new ConfigItemBoolean(info); + break; + case COLOR: + configItem = new ConfigItemColor(info); + break; + case FILE: + configItem = new ConfigItemBrowse(info, false); + break; + case DIRECTORY: + configItem = new ConfigItemBrowse(info, true); + break; + case COMBO_LIST: + configItem = new ConfigItemCombobox(info, true); + break; + case FIXED_LIST: + configItem = new ConfigItemCombobox(info, false); + break; + case INT: + configItem = new ConfigItemInteger(info); + break; + case PASSWORD: + configItem = new ConfigItemPassword(info); + break; + case LOCALE: + configItem = new ConfigItemLocale(info); + break; + case STRING: + default: + configItem = new ConfigItemString(info); + break; } - return minimumHeight; + configItem.init(nhgap); + return configItem; } /** @@ -598,4 +563,12 @@ public class ConfigItem> extends JPanel { button.setText(fallbackText); } } + + static private int getMinimumHeight() { + if (minimumHeight < 0) { + minimumHeight = new JTextField("Test").getMinimumSize().height; + } + + return minimumHeight; + } }