001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.conflict.tags; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trc; 006 007import java.awt.BorderLayout; 008import java.awt.FlowLayout; 009import java.awt.GridBagConstraints; 010import java.awt.GridBagLayout; 011import java.awt.Insets; 012import java.awt.event.ActionEvent; 013import java.awt.event.FocusAdapter; 014import java.awt.event.FocusEvent; 015import java.util.Collection; 016 017import javax.swing.AbstractAction; 018import javax.swing.AbstractButton; 019import javax.swing.BoxLayout; 020import javax.swing.ButtonModel; 021import javax.swing.JButton; 022import javax.swing.JCheckBox; 023import javax.swing.JLabel; 024import javax.swing.JPanel; 025import javax.swing.JScrollPane; 026import javax.swing.UIManager; 027import javax.swing.event.ChangeEvent; 028import javax.swing.event.ChangeListener; 029 030import org.openstreetmap.josm.command.ChangePropertyCommand; 031import org.openstreetmap.josm.command.Command; 032import org.openstreetmap.josm.data.osm.OsmPrimitive; 033import org.openstreetmap.josm.data.osm.Tag; 034import org.openstreetmap.josm.gui.MainApplication; 035import org.openstreetmap.josm.gui.layer.OsmDataLayer; 036import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField; 037import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; 038import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 039import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 040import org.openstreetmap.josm.tools.ImageProvider; 041 042/** 043 * This component presents the user with the ability to resolve a relation member conflict 044 * @see RelationMemberConflictResolverModel 045 */ 046public class RelationMemberConflictResolver extends JPanel { 047 048 private final AutoCompletingTextField tfRole = new AutoCompletingTextField(10); 049 private final AutoCompletingTextField tfKey = new AutoCompletingTextField(10); 050 private final AutoCompletingTextField tfValue = new AutoCompletingTextField(10); 051 private JCheckBox cbTagRelations; 052 private final RelationMemberConflictResolverModel model; 053 private final RelationMemberConflictResolverTable tblResolver; 054 private final JMultilineLabel lblHeader = new JMultilineLabel(""); 055 056 protected final void build() { 057 setLayout(new GridBagLayout()); 058 final JPanel pnl = new JPanel(new BorderLayout()); 059 pnl.add(lblHeader); 060 GridBagConstraints gc = new GridBagConstraints(); 061 gc.fill = GridBagConstraints.HORIZONTAL; 062 gc.weighty = 0.0; 063 gc.weightx = 1.0; 064 gc.insets = new Insets(5, 5, 5, 5); 065 add(pnl, gc); 066 067 gc.gridy = 1; 068 gc.weighty = 1.0; 069 gc.fill = GridBagConstraints.BOTH; 070 gc.insets = new Insets(0, 0, 0, 0); 071 add(new JScrollPane(tblResolver), gc); 072 073 final JPanel pnl2 = new JPanel(); 074 pnl2.setLayout(new BoxLayout(pnl2, BoxLayout.Y_AXIS)); 075 pnl2.add(buildRoleEditingPanel()); 076 pnl2.add(buildTagRelationsPanel()); 077 gc.gridy = 2; 078 gc.weighty = 0.0; 079 gc.fill = GridBagConstraints.HORIZONTAL; 080 add(pnl2, gc); 081 } 082 083 protected JPanel buildRoleEditingPanel() { 084 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT)); 085 pnl.add(new JLabel(tr("Role:"))); 086 pnl.add(tfRole); 087 tfRole.setToolTipText(tr("Enter a role for all relation memberships")); 088 pnl.add(new JButton(new ApplyRoleAction())); 089 tfRole.addActionListener(new ApplyRoleAction()); 090 tfRole.addFocusListener( 091 new FocusAdapter() { 092 @Override 093 public void focusGained(FocusEvent e) { 094 tfRole.selectAll(); 095 } 096 } 097 ); 098 return pnl; 099 } 100 101 protected JPanel buildTagRelationsPanel() { 102 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT)); 103 cbTagRelations = new JCheckBox(tr("Tag modified relations with ")); 104 cbTagRelations.addChangeListener(new ToggleTagRelationsAction()); 105 cbTagRelations.setToolTipText( 106 tr("<html>Select to enable entering a tag which will be applied<br>" 107 + "to all modified relations.</html>")); 108 pnl.add(cbTagRelations); 109 pnl.add(new JLabel(trc("tag", "Key:"))); 110 pnl.add(tfKey); 111 tfKey.setToolTipText(tr("<html>Enter a tag key, e.g. <strong><tt>fixme</tt></strong></html>")); 112 pnl.add(new JLabel(tr("Value:"))); 113 pnl.add(tfValue); 114 tfValue.setToolTipText(tr("<html>Enter a tag value, e.g. <strong><tt>check members</tt></strong></html>")); 115 cbTagRelations.setSelected(false); 116 tfKey.setEnabled(false); 117 tfValue.setEnabled(false); 118 return pnl; 119 } 120 121 /** 122 * Constructs a new {@code RelationMemberConflictResolver}. 123 * @param model model managing a list of conflicting relation members 124 * @since 7661 125 */ 126 public RelationMemberConflictResolver(RelationMemberConflictResolverModel model) { 127 this.model = model; 128 this.tblResolver = new RelationMemberConflictResolverTable(model); 129 build(); 130 } 131 132 /** 133 * Initializes for way combining. 134 */ 135 public void initForWayCombining() { 136 lblHeader.setText(tr("<html>The combined ways are members in one or more relations. " 137 + "Please decide whether you want to <strong>keep</strong> these memberships " 138 + "for the combined way or whether you want to <strong>remove</strong> them.<br>" 139 + "The default is to <strong>keep</strong> the first way and <strong>remove</strong> " 140 + "the other ways that are members of the same relation: the combined way will " 141 + "take the place of the original way in the relation." 142 + "</html>")); 143 invalidate(); 144 } 145 146 /** 147 * Initializes for node merging. 148 */ 149 public void initForNodeMerging() { 150 lblHeader.setText(tr("<html>The merged nodes are members in one or more relations. " 151 + "Please decide whether you want to <strong>keep</strong> these memberships " 152 + "for the target node or whether you want to <strong>remove</strong> them.<br>" 153 + "The default is to <strong>keep</strong> the first node and <strong>remove</strong> " 154 + "the other nodes that are members of the same relation: the target node will " 155 + "take the place of the original node in the relation." 156 + "</html>")); 157 invalidate(); 158 } 159 160 class ApplyRoleAction extends AbstractAction { 161 ApplyRoleAction() { 162 putValue(NAME, tr("Apply")); 163 new ImageProvider("ok").getResource().attachImageIcon(this); 164 putValue(SHORT_DESCRIPTION, tr("Apply this role to all members")); 165 } 166 167 @Override 168 public void actionPerformed(ActionEvent e) { 169 model.applyRole(tfRole.getText()); 170 } 171 } 172 173 class ToggleTagRelationsAction implements ChangeListener { 174 @Override 175 public void stateChanged(ChangeEvent e) { 176 ButtonModel buttonModel = ((AbstractButton) e.getSource()).getModel(); 177 tfKey.setEnabled(buttonModel.isSelected()); 178 tfValue.setEnabled(buttonModel.isSelected()); 179 tfKey.setBackground(buttonModel.isSelected() ? UIManager.getColor("TextField.background") : UIManager 180 .getColor("Panel.background")); 181 tfValue.setBackground(buttonModel.isSelected() ? UIManager.getColor("TextField.background") : UIManager 182 .getColor("Panel.background")); 183 } 184 } 185 186 public RelationMemberConflictResolverModel getModel() { 187 return model; 188 } 189 190 public Command buildTagApplyCommands(Collection<? extends OsmPrimitive> primitives) { 191 if (!cbTagRelations.isSelected()) 192 return null; 193 if (tfKey.getText().trim().isEmpty()) 194 return null; 195 if (tfValue.getText().trim().isEmpty()) 196 return null; 197 if (primitives == null || primitives.isEmpty()) 198 return null; 199 return new ChangePropertyCommand(primitives, Tag.removeWhiteSpaces(tfKey.getText()), Tag.removeWhiteSpaces(tfValue.getText())); 200 } 201 202 public void prepareForEditing() { 203 AutoCompletionList acList = new AutoCompletionList(); 204 OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer(); 205 if (editLayer != null) { 206 AutoCompletionManager.of(editLayer.data).populateWithMemberRoles(acList); 207 } 208 tfRole.setAutoCompletionList(acList); 209 AutoCompletingTextField editor = (AutoCompletingTextField) tblResolver.getColumnModel().getColumn(2).getCellEditor(); 210 if (editor != null) { 211 editor.setAutoCompletionList(acList); 212 } 213 AutoCompletionList acList2 = new AutoCompletionList(); 214 if (editLayer != null) { 215 AutoCompletionManager.of(editLayer.data).populateWithKeys(acList2); 216 } 217 tfKey.setAutoCompletionList(acList2); 218 } 219}