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