// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.turnrestrictions.editor; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.BorderLayout; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.Observable; import java.util.Observer; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; import org.openstreetmap.josm.gui.widgets.HtmlPanel; import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator; import org.openstreetmap.josm.tools.CheckParameterUtil; import org.openstreetmap.josm.tools.ImageProvider; /** * VehicleExceptionEditor is UI widget for editing exceptions to a turn restriction * based on vehicle types. * */ public class VehicleExceptionEditor extends JPanel implements Observer { private TurnRestrictionEditorModel model; private JCheckBox cbPsv; private JCheckBox cbBicyle; private JCheckBox cbMoped; private JCheckBox cbHgv; private JCheckBox cbMotorcar; private JTextField tfNonStandardValue; private ButtonGroup bgStandardOrNonStandard; private JRadioButton rbStandardException; private JRadioButton rbNonStandardException; private JPanel pnlStandard; private JPanel pnlNonStandard; private ExceptValueModel exceptValue = new ExceptValueModel(); private StandardVehicleTypeChangeListener svtChangeListener; private JPanel buildMessagePanel() { JPanel pnl = new JPanel(new BorderLayout()); HtmlPanel msg = new HtmlPanel(); pnl.add(msg, BorderLayout.CENTER); msg.setText( "<html><body>" + tr("Select the vehicle types this turn restriction is <strong>not</strong> applicable for.") + "</body></html>" ); return pnl; } private JPanel buildStandardInputPanel() { if (pnlStandard != null) return pnlStandard; svtChangeListener = new StandardVehicleTypeChangeListener(); GridBagConstraints gc = new GridBagConstraints(); gc.anchor = GridBagConstraints.NORTHWEST; gc.fill = GridBagConstraints.HORIZONTAL; gc.gridx = 0; gc.gridy = 0; pnlStandard = new JPanel(new GridBagLayout()); JLabel lbl; cbPsv = new JCheckBox(); cbPsv.addItemListener(svtChangeListener); lbl = new JLabel(); lbl.setText(tr("Public Service Vehicles")); lbl.setToolTipText(tr("Public service vehicles like buses, tramways, etc.")); lbl.setIcon(ImageProvider.get("vehicle", "psv")); gc.weightx = 0.0; pnlStandard.add(cbPsv, gc); gc.weightx = 1.0; gc.gridx++; pnlStandard.add(lbl, gc); cbHgv = new JCheckBox(); cbHgv.addItemListener(svtChangeListener); lbl = new JLabel(); lbl.setText(tr("Heavy Goods Vehicles")); lbl.setIcon(ImageProvider.get("vehicle", "hgv")); gc.weightx = 0.0; gc.gridx++; pnlStandard.add(cbHgv, gc); gc.weightx = 1.0; gc.gridx++; pnlStandard.add(lbl, gc); cbMotorcar = new JCheckBox(); cbMotorcar.addItemListener(svtChangeListener); lbl = new JLabel(); lbl.setText(tr("Motorcars")); lbl.setIcon(ImageProvider.get("vehicle", "motorcar")); gc.weightx = 0.0; gc.gridx = 0; gc.gridy = 1; pnlStandard.add(cbMotorcar, gc); gc.weightx = 1.0; gc.gridx++; pnlStandard.add(lbl, gc); cbBicyle = new JCheckBox(); cbBicyle.addItemListener(svtChangeListener); lbl = new JLabel(); lbl.setText(tr("Bicycles")); lbl.setIcon(ImageProvider.get("vehicle", "bicycle")); gc.weightx = 0.0; gc.gridx++; pnlStandard.add(cbBicyle, gc); gc.weightx = 1.0; gc.gridx++; pnlStandard.add(lbl, gc); cbMoped = new JCheckBox(); cbMoped.addItemListener(svtChangeListener); lbl = new JLabel(tr("Mopeds")); lbl.setIcon(ImageProvider.get("vehicle", "moped")); gc.weightx = 0.0; gc.gridx = 0; gc.gridy = 2; pnlStandard.add(cbMoped, gc); gc.weightx = 1.0; gc.gridx++; pnlStandard.add(lbl, gc); return pnlStandard; } private JPanel buildNonStandardInputPanel() { if (pnlNonStandard != null) return pnlNonStandard; pnlNonStandard = new JPanel(new GridBagLayout()); GridBagConstraints gc = new GridBagConstraints(); gc.anchor = GridBagConstraints.NORTHWEST; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 0.0; gc.insets = new Insets(0, 0, 4, 0); gc.gridx = 0; gc.gridy = 0; pnlNonStandard.add(new JLabel(tr("Value:")), gc); gc.gridx = 1; gc.weightx = 1.0; pnlNonStandard.add(tfNonStandardValue = new JTextField(), gc); SelectAllOnFocusGainedDecorator.decorate(tfNonStandardValue); NonStandardVehicleTypesHandler inputChangedHandler = new NonStandardVehicleTypesHandler(); tfNonStandardValue.addActionListener(inputChangedHandler); tfNonStandardValue.addFocusListener(inputChangedHandler); return pnlNonStandard; } /** * Builds the UI for entering standard values */ protected void buildStandard() { setLayout(new GridBagLayout()); GridBagConstraints gc = new GridBagConstraints(); gc.anchor = GridBagConstraints.NORTHWEST; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 1.0; gc.gridx = 0; gc.gridy = 0; add(buildMessagePanel(), gc); gc.gridy = 1; add(buildStandardInputPanel(), gc); } /** * Builds the UI for entering either standard or non-standard values */ protected void buildNonStandard() { setLayout(new GridBagLayout()); GridBagConstraints gc = new GridBagConstraints(); gc.anchor = GridBagConstraints.NORTHWEST; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 1.0; gc.gridx = 0; gc.gridy = 0; add(buildMessagePanel(), gc); gc.gridx = 0; gc.gridy = 1; gc.insets = new Insets(0, 0, 0, 0); add(rbStandardException = new JRadioButton(tr("Use standard exceptions")), gc); gc.gridx = 0; gc.gridy = 2; gc.insets = new Insets(0, 20, 0, 0); add(buildStandardInputPanel(), gc); gc.gridx = 0; gc.gridy = 3; gc.insets = new Insets(0, 0, 0, 0); add(rbNonStandardException = new JRadioButton(tr("Use non-standard exceptions")), gc); gc.gridx = 0; gc.gridy = 4; gc.insets = new Insets(0, 20, 0, 0); add(buildNonStandardInputPanel(), gc); bgStandardOrNonStandard = new ButtonGroup(); bgStandardOrNonStandard.add(rbNonStandardException); bgStandardOrNonStandard.add(rbStandardException); StandardNonStandardChangeHandler changeHandler = new StandardNonStandardChangeHandler(); rbNonStandardException.addItemListener(changeHandler); rbStandardException.addItemListener(changeHandler); } protected void build() { removeAll(); buildNonStandardInputPanel(); buildStandardInputPanel(); if (exceptValue.isStandard()) { buildStandard(); } else { buildNonStandard(); } init(); invalidate(); } protected void init() { try { // temporarily disable the checkbox listeners while initializing the // checkboxes with the input value this.svtChangeListener.setEnabled(false); cbPsv.setSelected(exceptValue.isVehicleException("psv")); cbBicyle.setSelected(exceptValue.isVehicleException("bicycle")); cbMoped.setSelected(exceptValue.isVehicleException("moped")); cbMotorcar.setSelected(exceptValue.isVehicleException("motorcar")); cbHgv.setSelected(exceptValue.isVehicleException("hgv")); } finally { this.svtChangeListener.setEnabled(true); } if (!exceptValue.isStandard()) { rbNonStandardException.setSelected(true); tfNonStandardValue.setText(exceptValue.getValue()); setEnabledNonStandardInputPanel(true); setEnabledStandardInputPanel(false); } else { setEnabledNonStandardInputPanel(false); setEnabledStandardInputPanel(true); } } protected void setEnabledStandardInputPanel(boolean enabled) { for (Component c: pnlStandard.getComponents()) { c.setEnabled(enabled); } } protected void setEnabledNonStandardInputPanel(boolean enabled) { for (Component c: pnlNonStandard.getComponents()) { c.setEnabled(enabled); } } /** * Creates the editor * * @param model the editor model. Must not be null. * @throws IllegalArgumentException thrown if {@code model} is null */ public VehicleExceptionEditor(TurnRestrictionEditorModel model) throws IllegalArgumentException { CheckParameterUtil.ensureParameterNotNull(model, "model"); this.model = model; build(); model.addObserver(this); } /* ------------------------------------------------------------------------------------ */ /* interface Observer */ /* ------------------------------------------------------------------------------------ */ @Override public void update(Observable o, Object arg) { if (!this.exceptValue.equals(model.getExcept())) { this.exceptValue = model.getExcept(); build(); } } /* ------------------------------------------------------------------------------------ */ /* inner classes */ /* ------------------------------------------------------------------------------------ */ class StandardNonStandardChangeHandler implements ItemListener { @Override public void itemStateChanged(ItemEvent e) { if (rbNonStandardException.isSelected()) { setEnabledNonStandardInputPanel(true); setEnabledStandardInputPanel(false); exceptValue.setStandard(false); } else { setEnabledNonStandardInputPanel(false); setEnabledStandardInputPanel(true); exceptValue.setStandard(true); } model.setExcept(exceptValue); } } class StandardVehicleTypeChangeListener implements ItemListener { private boolean enabled = true; public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public void itemStateChanged(ItemEvent e) { if (!enabled) return; exceptValue.setVehicleException("bicycle", cbBicyle.isSelected()); exceptValue.setVehicleException("moped", cbMoped.isSelected()); exceptValue.setVehicleException("hgv", cbHgv.isSelected()); exceptValue.setVehicleException("psv", cbPsv.isSelected()); exceptValue.setVehicleException("motorcar", cbMotorcar.isSelected()); model.setExcept(exceptValue); } } class NonStandardVehicleTypesHandler implements ActionListener, FocusListener { public void persist() { exceptValue.setValue(tfNonStandardValue.getText()); model.setExcept(exceptValue); } @Override public void focusGained(FocusEvent e) {} @Override public void focusLost(FocusEvent e) { persist(); } @Override public void actionPerformed(ActionEvent e) { persist(); } } }