001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Dimension; 007import java.awt.GridBagLayout; 008import java.awt.Rectangle; 009import java.awt.event.ActionEvent; 010import java.awt.event.KeyEvent; 011import java.awt.event.KeyListener; 012import java.awt.event.MouseAdapter; 013import java.awt.event.MouseEvent; 014import java.util.List; 015import java.util.Locale; 016import java.util.Map; 017 018import javax.swing.AbstractAction; 019import javax.swing.ImageIcon; 020import javax.swing.JMenuItem; 021import javax.swing.JOptionPane; 022import javax.swing.JPanel; 023import javax.swing.JPopupMenu; 024import javax.swing.JScrollPane; 025import javax.swing.JTree; 026import javax.swing.tree.DefaultMutableTreeNode; 027import javax.swing.tree.TreePath; 028 029import org.openstreetmap.josm.actions.ValidateAction; 030import org.openstreetmap.josm.data.validation.OsmValidator; 031import org.openstreetmap.josm.data.validation.TestError; 032import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 033import org.openstreetmap.josm.gui.ExtendedDialog; 034import org.openstreetmap.josm.gui.MainApplication; 035import org.openstreetmap.josm.gui.MapFrame; 036import org.openstreetmap.josm.gui.util.GuiHelper; 037import org.openstreetmap.josm.tools.GBC; 038import org.openstreetmap.josm.tools.ImageProvider; 039import org.openstreetmap.josm.tools.Logging; 040 041/** 042 * A management window for the validator's ignorelist 043 * @author Taylor Smock 044 * @since 14828 045 */ 046public class ValidatorListManagementDialog extends ExtendedDialog { 047 enum BUTTONS { 048 OK(0, tr("OK"), new ImageProvider("ok")), 049 CANCEL(1, tr("Cancel"), new ImageProvider("cancel")); 050 051 private int index; 052 private String name; 053 private ImageIcon icon; 054 055 BUTTONS(int index, String name, ImageProvider image) { 056 this.index = index; 057 this.name = name; 058 Dimension dim = new Dimension(); 059 ImageIcon sizeto = new ImageProvider("ok").getResource().getImageIcon(); 060 dim.setSize(-1, sizeto.getIconHeight()); 061 this.icon = image.getResource().getImageIcon(dim); 062 } 063 064 public ImageIcon getImageIcon() { 065 return icon; 066 } 067 068 public int getIndex() { 069 return index; 070 } 071 072 public String getName() { 073 return name; 074 } 075 } 076 077 private static final String[] BUTTON_TEXTS = {BUTTONS.OK.getName(), BUTTONS.CANCEL.getName()}; 078 079 private static final ImageIcon[] BUTTON_IMAGES = {BUTTONS.OK.getImageIcon(), BUTTONS.CANCEL.getImageIcon()}; 080 081 private final JPanel panel = new JPanel(new GridBagLayout()); 082 083 private final JTree ignoreErrors; 084 085 private final String type; 086 087 /** 088 * Create a new {@link ValidatorListManagementDialog} 089 * @param type The type of list to create (first letter may or may not be 090 * capitalized, it is put into all lowercase after building the title) 091 */ 092 public ValidatorListManagementDialog(String type) { 093 super(MainApplication.getMainFrame(), tr("Validator {0} List Management", type), BUTTON_TEXTS, false); 094 this.type = type.toLowerCase(Locale.ENGLISH); 095 setButtonIcons(BUTTON_IMAGES); 096 097 ignoreErrors = buildList(); 098 JScrollPane scroll = GuiHelper.embedInVerticalScrollPane(ignoreErrors); 099 100 panel.add(scroll, GBC.eol().fill(GBC.BOTH).anchor(GBC.CENTER)); 101 setContent(panel); 102 setDefaultButton(1); 103 setupDialog(); 104 setModal(true); 105 showDialog(); 106 } 107 108 @Override 109 public void buttonAction(int buttonIndex, ActionEvent evt) { 110 // Currently OK/Cancel buttons do nothing 111 final int answer; 112 if (buttonIndex == BUTTONS.OK.getIndex()) { 113 Map<String, String> errors = OsmValidator.getIgnoredErrors(); 114 Map<String, String> tree = OsmValidator.buildIgnore(ignoreErrors); 115 if (!errors.equals(tree)) { 116 answer = rerunValidatorPrompt(); 117 if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) { 118 OsmValidator.resetErrorList(); 119 tree.forEach(OsmValidator::addIgnoredError); 120 OsmValidator.saveIgnoredErrors(); 121 OsmValidator.initialize(); 122 } 123 } 124 dispose(); 125 } else { 126 super.buttonAction(buttonIndex, evt); 127 } 128 } 129 130 /** 131 * Build a JTree with a list 132 * @return <type>list as a {@code JTree} 133 */ 134 public JTree buildList() { 135 JTree tree; 136 137 if ("ignore".equals(type)) { 138 tree = OsmValidator.buildJTreeList(); 139 } else { 140 Logging.error(tr("Cannot understand the following type: {0}", type)); 141 return null; 142 } 143 tree.setRootVisible(false); 144 tree.setShowsRootHandles(true); 145 tree.addMouseListener(new MouseAdapter() { 146 @Override 147 public void mousePressed(MouseEvent e) { 148 process(e); 149 } 150 151 @Override 152 public void mouseReleased(MouseEvent e) { 153 process(e); 154 } 155 156 private void process(MouseEvent e) { 157 if (e.isPopupTrigger()) { 158 TreePath[] paths = tree.getSelectionPaths(); 159 if (paths == null) return; 160 Rectangle bounds = tree.getUI().getPathBounds(tree, paths[0]); 161 if (bounds != null) { 162 JPopupMenu menu = new JPopupMenu(); 163 JMenuItem delete = new JMenuItem(new AbstractAction(tr("Don''t ignore")) { 164 @Override 165 public void actionPerformed(ActionEvent e1) { 166 deleteAction(tree, paths); 167 } 168 }); 169 menu.add(delete); 170 menu.show(e.getComponent(), e.getX(), e.getY()); 171 } 172 } 173 } 174 }); 175 176 tree.addKeyListener(new KeyListener() { 177 178 @Override 179 public void keyTyped(KeyEvent e) { 180 // Do nothing 181 } 182 183 @Override 184 public void keyPressed(KeyEvent e) { 185 // Do nothing 186 } 187 188 @Override 189 public void keyReleased(KeyEvent e) { 190 TreePath[] paths = tree.getSelectionPaths(); 191 if (e.getKeyCode() == KeyEvent.VK_DELETE && paths != null) { 192 deleteAction(tree, paths); 193 } 194 } 195 }); 196 return tree; 197 } 198 199 private static void deleteAction(JTree tree, TreePath[] paths) { 200 for (TreePath path : paths) { 201 tree.clearSelection(); 202 tree.addSelectionPath(path); 203 DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); 204 DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); 205 node.removeAllChildren(); 206 while (node.getChildCount() == 0) { 207 node.removeFromParent(); 208 node = parent; 209 if (parent == null || parent.isRoot()) break; 210 parent = (DefaultMutableTreeNode) node.getParent(); 211 } 212 } 213 tree.updateUI(); 214 } 215 216 /** 217 * Prompt to rerun the validator when the ignore list changes 218 * @return {@code JOptionPane.YES_OPTION}, {@code JOptionPane.NO_OPTION}, 219 * or {@code JOptionPane.CANCEL_OPTION} 220 */ 221 public int rerunValidatorPrompt() { 222 MapFrame map = MainApplication.getMap(); 223 List<TestError> errors = map.validatorDialog.tree.getErrors(); 224 ValidateAction validateAction = ValidatorDialog.validateAction; 225 if (!validateAction.isEnabled() || errors == null || errors.isEmpty()) return JOptionPane.NO_OPTION; 226 final int answer = ConditionalOptionPaneUtil.showOptionDialog( 227 "rerun_validation_when_ignorelist_changed", 228 MainApplication.getMainFrame(), 229 tr("{0}Should the validation be rerun?{1}", "<hmtl><h3>", "</h3></html>"), 230 tr("Ignored error filter changed"), 231 JOptionPane.YES_NO_CANCEL_OPTION, 232 JOptionPane.QUESTION_MESSAGE, 233 null, 234 null); 235 if (answer == JOptionPane.YES_OPTION) { 236 validateAction.doValidate(true); 237 } 238 return answer; 239 } 240}