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