diff --git a/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java b/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java index 5752c0b3f..aba36bd35 100644 --- a/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java @@ -13,6 +13,7 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; +import java.util.HashMap; /** * Dialog used to edit Attributes. @@ -32,6 +33,8 @@ public class AttributeDialog extends JDialog { private final ElementAttributes originalAttributes; private final ElementAttributes modifiedAttributes; private final JPanel buttonPanel; + private final GBC constrains; + private HashMap checkBoxes; private JComponent topMostTextComponent; private VisualElement visualElement; private boolean okPressed = false; @@ -44,7 +47,7 @@ public class AttributeDialog extends JDialog { * @param elementAttributes the data stored */ public AttributeDialog(Component parent, java.util.List list, ElementAttributes elementAttributes) { - this(parent, null, list, elementAttributes); + this(parent, null, list, elementAttributes, false); } /** @@ -56,6 +59,19 @@ public class AttributeDialog extends JDialog { * @param elementAttributes the data stored */ public AttributeDialog(Component parent, Point pos, java.util.List list, ElementAttributes elementAttributes) { + this(parent, pos, list, elementAttributes, false); + } + + /** + * Creates a new instance + * + * @param parent the parent + * @param pos the position to pop up the dialog + * @param list the list of keys which are to edit + * @param elementAttributes the data stored + * @param addCheckBoxes add checkboxes behind the attributes + */ + public AttributeDialog(Component parent, Point pos, java.util.List list, ElementAttributes elementAttributes, boolean addCheckBoxes) { super(SwingUtilities.getWindowAncestor(parent), Lang.get("attr_dialogTitle"), ModalityType.APPLICATION_MODAL); this.parent = parent; this.pos = pos; @@ -63,17 +79,27 @@ public class AttributeDialog extends JDialog { this.modifiedAttributes = new ElementAttributes(elementAttributes); setDefaultCloseOperation(DISPOSE_ON_CLOSE); - panel = new JPanel(new DialogLayout()); + panel = new JPanel(new GridBagLayout()); getContentPane().add(new JScrollPane(panel)); editors = new ArrayList<>(); topMostTextComponent = null; + constrains = new GBC().inset(3).fill(); for (Key key : list) { Editor e = EditorFactory.INSTANCE.create(key, modifiedAttributes.get(key)); editors.add(new EditorHolder(e, key)); - e.addToPanel(panel, key, modifiedAttributes, this); + e.addToPanel(panel, key, modifiedAttributes, this, constrains); + if (addCheckBoxes) { + if (checkBoxes == null) + checkBoxes = new HashMap<>(); + JCheckBox checkBox = new JCheckBox(); + checkBox.setToolTipText(Lang.get("msg_modifyThisAttribute")); + checkBoxes.put(key, checkBox); + panel.add(checkBox, constrains.x(2)); + } + constrains.nextRow(); if (topMostTextComponent == null && e instanceof EditorFactory.StringEditor) topMostTextComponent = ((EditorFactory.StringEditor) e).getTextComponent(); @@ -119,6 +145,13 @@ public class AttributeDialog extends JDialog { dispose(); } + /** + * @return the keys check boxes + */ + public HashMap getCheckBoxes() { + return checkBoxes; + } + /** * Adds a button to this dialog * @@ -127,13 +160,14 @@ public class AttributeDialog extends JDialog { * @return this for chained calls */ public AttributeDialog addButton(String label, ToolTipAction action) { - panel.add(new JLabel(label), DialogLayout.LABEL); - panel.add(action.createJButton(), DialogLayout.INPUT); + panel.add(new JLabel(label), constrains); + panel.add(action.createJButton(), constrains.x(1)); + constrains.nextRow(); return this; } /** - * Adds a button to the bottom of this dialog + * Adds a button to the botton of this dialog * * @param action the action * @return this for chained calls diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index b771a4f64..b66728afa 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -7,6 +7,7 @@ import de.neemann.digital.core.element.*; import de.neemann.digital.core.io.In; import de.neemann.digital.core.io.InValue; import de.neemann.digital.draw.elements.*; +import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.draw.shapes.InputShape; import de.neemann.digital.gui.components.modification.*; import de.neemann.digital.draw.graphics.*; @@ -33,9 +34,7 @@ import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; +import java.util.*; import java.util.List; import static de.neemann.digital.draw.shapes.GenericShape.SIZE; @@ -982,45 +981,49 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe try { ArrayList keyList = new ArrayList<>(); ArrayList elementList = new ArrayList<>(); + HashMap useKeyMap = new HashMap<>(); + ElementAttributes attr = new ElementAttributes(); for (VisualElement ve : circuit.getElements()) if (ve.matches(min, max)) { elementList.add(ve); for (Key k : library.getElementType(ve.getElementName()).getAttributeList()) { - if (k.isGroupEditAllowed() && !keyList.contains(k)) - keyList.add(k); - } - } - - if (keyList.size() > 0) { - Key key = null; - if (keyList.size() == 1) - key = keyList.get(0); - else { - ItemPicker ip = new ItemPicker(this, keyList); - if (ip.showDialog()) - key = ip.getSelected(); - } - if (key != null) { - keyList.clear(); - keyList.add(key); - ElementAttributes attr = new ElementAttributes(); - AttributeDialog attributeDialog = new AttributeDialog(this, keyList, attr); - ElementAttributes mod = attributeDialog.showDialog(); - if (attributeDialog.isOkPressed()) { - if (mod == null) mod = attr; - - Modifications.Builder modBuilder = new Modifications.Builder(Lang.get("mod_groupEdit")); - Object newVal = mod.get(key); - for (VisualElement ve : elementList) { - if (library.getElementType(ve.getElementName()).getAttributeList().contains(key)) { - if (!ve.getElementAttributes().get(key).equals(newVal)) - modBuilder.add(new ModifyAttribute<>(ve, key, newVal)); + if (k.isGroupEditAllowed()) { + if (keyList.contains(k)) { + if (!ve.getElementAttributes().get(k).equals(attr.get(k))) { + useKeyMap.put(k, false); + } + } else { + keyList.add(k); + attr.set(k, ve.getElementAttributes().get(k)); + useKeyMap.put(k, true); } } - modify(modBuilder.build()); } } + + if (keyList.size()>0) { + AttributeDialog ad = new AttributeDialog(this, null, keyList, attr, true); + for (Map.Entry u : useKeyMap.entrySet()) + ad.getCheckBoxes().get(u.getKey()).setSelected(u.getValue()); + ElementAttributes mod = ad.showDialog(); + if (ad.isOkPressed()) { + if (mod == null) mod = attr; + + Modifications.Builder modBuilder = new Modifications.Builder(Lang.get("mod_groupEdit")); + for (Key key : keyList) + if (ad.getCheckBoxes().get(key).isSelected()) { + Object newVal = mod.get(key); + for (VisualElement ve : elementList) { + if (library.getElementType(ve.getElementName()).getAttributeList().contains(key)) { + if (!ve.getElementAttributes().get(key).equals(newVal)) + modBuilder.add(new ModifyAttribute<>(ve, key, newVal)); + } + } + } + modify(modBuilder.build()); + } } + } catch (ElementNotFoundException e) { // Do nothing if an element is not in library } diff --git a/src/main/java/de/neemann/digital/gui/components/DialogLayout.java b/src/main/java/de/neemann/digital/gui/components/DialogLayout.java deleted file mode 100644 index 33647f716..000000000 --- a/src/main/java/de/neemann/digital/gui/components/DialogLayout.java +++ /dev/null @@ -1,279 +0,0 @@ -package de.neemann.digital.gui.components; - -import java.awt.*; -import java.util.ArrayList; - -/** - * Layout manager used in teh GUI dialog - * - * @author hneemann - */ -public class DialogLayout implements LayoutManager { - /** - * Used to indicate the element as a label - */ - public static final String LABEL = "label"; - /** - * Used to indicate the element as a label - */ - public static final String INPUT = "input"; - /** - * Used to indicate that the element should use the space for label and input (e.g. CheckBoxes) - */ - public static final String BOTH = "both"; - /** - * Use label and input column, but indent field - */ - public static final String BOTHINDENT = "indent"; - /** - * Use label and input column, height is adjustable - */ - public static final String BOTHDYNAMIC = "bothDyn"; - private static final int BOTHOFFSET = 20; - private static final int SPACEX = 5; - private static final int SPACEY = 5; - private ArrayList lines; - private int label; - private boolean initialized = false; - private int dynCount; - - /** - * @return true if the are fields with - */ - public boolean isDynamic() { - initialize(); - return dynCount > 0; - } - - private static class Line { - private boolean both; - private Component label; - private Component input; - private boolean indent; - private boolean dynamic; - - Line(Component both, boolean indent) { - this.label = both; - this.both = true; - this.indent = indent; - } - - Line(Component component, String s) { - both = false; - if (LABEL.equals(s)) label = component; - else if (INPUT.equals(s)) input = component; - else throw new IllegalArgumentException(); - } - - public Component getLabel() { - return label; - } - - public Component getInput() { - return input; - } - - public void setLabel(Component component) { - label = component; - } - - public boolean isLabelFree() { - return !both && label == null; - } - - public boolean isInputFree() { - return !both && input == null; - } - - public void setInput(Component input) { - this.input = input; - } - - public boolean isBoth() { - return both; - } - - public int getHeight() { - if (both) return label.getPreferredSize().height; - else { - int height = 0; - if (label != null) { - int h = label.getPreferredSize().height; - if (h > height) height = h; - } - if (input != null) { - int h = input.getPreferredSize().height; - if (h > height) height = h; - } - return height; - } - } - - public boolean isIndent() { - return indent; - } - - public void setDynamic(boolean dynamic) { - this.dynamic = dynamic; - } - - public boolean isDynamic() { - return dynamic; - } - - public void initialize() { - if (both) { - if (label instanceof Container) - initContainer((Container) label); - } - } - - private void initContainer(Container container) { - for (int i = 0; i < container.getComponentCount(); i++) { - Component c = container.getComponent(i); - if (c instanceof Container /*&& !(((Container)c).getLayout() instanceof DialogLayout)*/) - initContainer((Container) c); - } - - - LayoutManager lm = container.getLayout(); - if (lm instanceof DialogLayout) { - DialogLayout dl = (DialogLayout) lm; - if (dl.isDynamic()) setDynamic(true); - } - } - } - - /** - * Creates a new Layout for the edit dialog - */ - public DialogLayout() { - lines = new ArrayList(); - } - - - @Override - public void addLayoutComponent(String s, Component component) { - if (BOTH.equals(s)) { - lines.add(new Line(component, false)); - } else if (BOTHDYNAMIC.equals(s)) { - Line l = new Line(component, false); - lines.add(l); - l.setDynamic(true); - } else if (BOTHINDENT.equals(s)) { - lines.add(new Line(component, true)); - } else if (LABEL.equals(s)) { - if (lines.size() > 0 && lines.get(lines.size() - 1).isLabelFree()) - lines.get(lines.size() - 1).setLabel(component); - else - lines.add(new Line(component, LABEL)); - } else if (INPUT.equals(s)) { - addInput(component); - } else - throw new IllegalArgumentException(); - } - - private Line addInput(Component component) { - Line l; - if (lines.size() > 0 && lines.get(lines.size() - 1).isInputFree()) { - l = lines.get(lines.size() - 1); - l.setInput(component); - } else { - l = new Line(component, INPUT); - lines.add(l); - } - - return l; - } - - @Override - public void removeLayoutComponent(Component component) { - throw new UnsupportedOperationException(); - } - - @Override - public Dimension preferredLayoutSize(Container container) { - return minimumLayoutSize(container); - } - - private void initialize() { - if (!initialized) { - dynCount = 0; - for (Line l : lines) { - l.initialize(); - if (l.isDynamic()) dynCount++; - } - initialized = true; - } - } - - @Override - public Dimension minimumLayoutSize(Container container) { - initialize(); - - int both = 0; - label = 0; - int input = 0; - int height = 0; - for (Line l : lines) { - if (l.isBoth()) { - int w = l.getLabel().getPreferredSize().width; - if (l.isIndent()) w += BOTHOFFSET; - if (w > both) both = w; - } else { - if (l.getLabel() != null) { - int w = l.getLabel().getPreferredSize().width; - if (w > label) label = w; - } - if (l.getInput() != null) { - int w = l.getInput().getPreferredSize().width; - if (w > input) input = w; - } - } - height += l.getHeight(); - } - Insets in = container.getInsets(); - return new Dimension(Math.max(both + SPACEX * 2, label + input + SPACEX * 3) + in.left + in.right, height + in.top + in.bottom + (lines.size() + 1) * SPACEY); - } - - @Override - public void layoutContainer(Container container) { - Dimension minSize = minimumLayoutSize(container); - Insets in = container.getInsets(); - - int dynAddendum = 0; - if (dynCount > 0) dynAddendum = (container.getSize().height - minSize.height) / dynCount; - - - int width = container.getSize().width; - - int top = in.top + SPACEY; - for (Line l : lines) { - int height = l.getHeight(); - if (l.isDynamic()) height += dynAddendum; - if (l.isBoth()) { - Component c = l.getLabel(); - if (l.isIndent()) { - c.setLocation(in.left + BOTHOFFSET + SPACEX, top); - c.setSize(width - BOTHOFFSET - SPACEX * 2 - in.left - in.right, height); - } else { - c.setLocation(in.left + SPACEX, top); - c.setSize(width - SPACEX * 2 - in.left - in.right, height); - } - } else { - Component labelComp = l.getLabel(); - if (labelComp != null) { - labelComp.setLocation(in.left + SPACEX, top); - labelComp.setSize(label, height); - } - Component inputComp = l.getInput(); - if (inputComp != null) { - inputComp.setLocation(label + in.left + SPACEX * 2, top); - inputComp.setSize(width - label - SPACEX * 3 - in.left - in.right, height); - } - } - - top += height + SPACEY; - } - } -} diff --git a/src/main/java/de/neemann/digital/gui/components/Editor.java b/src/main/java/de/neemann/digital/gui/components/Editor.java index 45e2e413e..fa272f1a3 100644 --- a/src/main/java/de/neemann/digital/gui/components/Editor.java +++ b/src/main/java/de/neemann/digital/gui/components/Editor.java @@ -23,6 +23,7 @@ public interface Editor { * @param key the key which is to edit * @param elementAttributes the attributes * @param dialog the containing dialog + * @param constrains the constrains used to place the components in the panel */ - void addToPanel(JPanel panel, Key key, ElementAttributes elementAttributes, AttributeDialog dialog); + void addToPanel(JPanel panel, Key key, ElementAttributes elementAttributes, AttributeDialog dialog, GBC constrains); } diff --git a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java index 9697e762c..5e58af344 100644 --- a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java +++ b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java @@ -97,17 +97,21 @@ public final class EditorFactory { private boolean labelAtTop = false; @Override - public void addToPanel(JPanel panel, Key key, ElementAttributes elementAttributes, AttributeDialog attributeDialog) { + public void addToPanel(JPanel panel, Key key, ElementAttributes elementAttributes, AttributeDialog attributeDialog, GBC constrains) { this.attributeDialog = attributeDialog; JLabel label = new JLabel(key.getName() + ": "); - if (labelAtTop) - label.setVerticalAlignment(JLabel.TOP); final String description = new LineBreaker().toHTML().breakLines(key.getDescription()); label.setToolTipText(description); - panel.add(label, DialogLayout.LABEL); JComponent component = getComponent(elementAttributes); component.setToolTipText(description); - panel.add(component, DialogLayout.INPUT); + if (labelAtTop) { + panel.add(label, constrains.width(2)); + constrains.nextRow(); + panel.add(component, constrains.width(2).dynamicHeight()); + } else { + panel.add(label, constrains); + panel.add(component, constrains.x(1).dynamicWidth()); + } } /** @@ -252,8 +256,8 @@ public final class EditorFactory { } @Override - public void addToPanel(JPanel panel, Key key, ElementAttributes elementAttributes, AttributeDialog attributeDialog) { - panel.add(bool, DialogLayout.BOTH); + public void addToPanel(JPanel panel, Key key, ElementAttributes elementAttributes, AttributeDialog attributeDialog, GBC constrains) { + panel.add(bool, constrains.width(2)); } } diff --git a/src/main/java/de/neemann/digital/gui/components/GBC.java b/src/main/java/de/neemann/digital/gui/components/GBC.java new file mode 100644 index 000000000..ef65eace7 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/GBC.java @@ -0,0 +1,144 @@ +package de.neemann.digital.gui.components; + +import java.awt.*; + +/** + * More simple to use GridBagConstrains + */ +public class GBC extends GridBagConstraints { + + /** + * Creates a new instance. + * Position is set to (0,0) + */ + public GBC() { + gridx = 0; + gridy = 0; + } + + private GBC copy() { + GBC n = new GBC(); + n.gridx = gridx; + n.gridy = gridy; + n.gridwidth = gridwidth; + n.gridheight = gridheight; + n.weightx = weightx; + n.weighty = weighty; + n.ipadx = ipadx; + n.ipady = ipady; + n.fill = fill; + n.insets = insets; + return n; + } + + /** + * Sets the position + * + * @param x x position + * @param y y position + * @return the new created GBC instance + */ + public GBC pos(int x, int y) { + GBC c = copy(); + c.gridx = x; + c.gridy = y; + return c; + } + + /** + * Sets the position + * + * @param x x position + * @return the new created GBC instance + */ + public GBC x(int x) { + GBC c = copy(); + c.gridx = x; + return c; + } + + /** + * Sets the width + * + * @param x width + * @return the new created GBC instance + */ + public GBC width(int x) { + GBC c = copy(); + c.gridwidth = x; + return c; + } + + /** + * Sets a dynamic height + * + * @return the new created GBC instance + */ + public GBC dynamicHeight() { + GBC c = copy(); + c.weighty = 1; + return c; + } + + /** + * Sets a dynamic width + * + * @return the new created GBC instance + */ + public GBC dynamicWidth() { + GBC c = copy(); + c.weightx = 1; + return c; + } + + + /** + * Sets the padding + * + * @param x x padding + * @param y y padding + * @return the new created GBC instance + */ + public GBC pad(int x, int y) { + GBC c = copy(); + c.ipadx = x; + c.ipady = y; + return c; + } + + /** + * Sets the fill attribute to BOTH + * + * @return the new created GBC instance + */ + public GBC fill() { + GBC c = copy(); + c.fill = GridBagConstraints.BOTH; + return c; + } + + /** + * Sets insets to a border of width b + * + * @param b border width + * @return the new created GBC instance + */ + public GBC inset(int b) { + GBC c = copy(); + c.insets = new Insets(b, b, b, b); + return c; + } + + /** + * Increases the row. + * Does not create a new instance + * + * @return this for chained calls + */ + public GBC nextRow() { + gridx = 0; + gridy++; + return this; + } + +} diff --git a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java index 8729a030e..2a6e3017e 100644 --- a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java @@ -89,11 +89,13 @@ public final class SingleValueDialog extends JDialog implements ModelStateObserv setLongToDialog(editValue); }); - JPanel panel = new JPanel(new DialogLayout()); - panel.add(formatComboBox, DialogLayout.LABEL); - panel.add(textField, DialogLayout.INPUT); - panel.add(new JLabel(Lang.get("attr_dialogBinary")), DialogLayout.LABEL); - panel.add(createCheckBoxPanel(value.getBits(), editValue), DialogLayout.INPUT); + JPanel panel = new JPanel(new GridBagLayout()); + GBC constr = new GBC().inset(3).fill(); + panel.add(formatComboBox, constr); + panel.add(textField, constr.dynamicWidth().x(1)); + constr.nextRow(); + panel.add(new JLabel(Lang.get("attr_dialogBinary")), constr); + panel.add(createCheckBoxPanel(value.getBits(), editValue), constr.dynamicWidth().x(1)); getContentPane().add(panel); textField.getDocument().addDocumentListener(new MyDocumentListener(() -> setStringToDialog(textField.getText()))); diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 15c0cf90a..aec4f8718 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -1015,6 +1015,7 @@ eine <a href="https://github.com/hneemann/Digital/issues/new?labels=enha Es kann nur eine fehlerfreie Schaltung exportiert werden! Keine KV-Tafel verfügbar! Daten werden nicht mehr aktualisiert! + Diesen Wert setzen Ok diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 015169bc5..269da1067 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -996,6 +996,7 @@ an <a href="https://github.com/hneemann/Digital/issues/new?labels=enhanc You can only export a circuit without errors! No KV map available! Data will not be updated anymore! + Modify this Value Ok