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