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