uses check boxes for the groupEdit

This commit is contained in:
hneemann 2017-09-12 09:22:52 +02:00
parent a950dff866
commit 60c9898d4f
9 changed files with 242 additions and 331 deletions

View File

@ -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<Key, JCheckBox> 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<Key> 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<Key> 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<Key> 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<Key, JCheckBox> 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

View File

@ -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<Key> keyList = new ArrayList<>();
ArrayList<VisualElement> elementList = new ArrayList<>();
HashMap<Key, Boolean> 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<Key> ip = new ItemPicker<Key>(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<Key, Boolean> 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
}

View File

@ -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<Line> 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<Line>();
}
@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;
}
}
}

View File

@ -23,6 +23,7 @@ public interface Editor<T> {
* @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);
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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())));

View File

@ -1015,6 +1015,7 @@ eine &lt;a href=&quot;https://github.com/hneemann/Digital/issues/new?labels=enha
<string name="msg_modelHasErrors">Es kann nur eine fehlerfreie Schaltung exportiert werden!</string>
<string name="msg_noKVMapAvailable">Keine KV-Tafel verfügbar!</string>
<string name="msg_dataNotUpdatedAnymore">Daten werden nicht mehr aktualisiert!</string>
<string name="msg_modifyThisAttribute">Diesen Wert setzen</string>
<string name="ok">Ok</string>
<string name="rot_0"></string>

View File

@ -996,6 +996,7 @@ an &lt;a href=&quot;https://github.com/hneemann/Digital/issues/new?labels=enhanc
<string name="msg_modelHasErrors">You can only export a circuit without errors!</string>
<string name="msg_noKVMapAvailable">No KV map available!</string>
<string name="msg_dataNotUpdatedAnymore">Data will not be updated anymore!</string>
<string name="msg_modifyThisAttribute">Modify this Value</string>
<string name="ok">Ok</string>
<string name="rot_0"></string>