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 AutoCompletingTextField tfRole; 043 private AutoCompletingTextField tfKey; 044 private AutoCompletingTextField tfValue; 045 private JCheckBox cbTagRelations; 046 private final RelationMemberConflictResolverModel model; 047 private RelationMemberConflictResolverTable tblResolver; 048 private JMultilineLabel lblHeader; 049 050 protected final void build() { 051 setLayout(new GridBagLayout()); 052 final JPanel pnl = new JPanel(new BorderLayout()); 053 pnl.add(lblHeader = new JMultilineLabel("")); 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 = new RelationMemberConflictResolverTable(model)), 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 = new AutoCompletingTextField(10)); 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 = new AutoCompletingTextField(10)); 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 = new AutoCompletingTextField(10)); 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 build(); 123 } 124 125 /** 126 * Initializes for way combining. 127 */ 128 public void initForWayCombining() { 129 lblHeader.setText(tr("<html>The combined ways are members in one or more relations. " 130 + "Please decide whether you want to <strong>keep</strong> these memberships " 131 + "for the combined way or whether you want to <strong>remove</strong> them.<br>" 132 + "The default is to <strong>keep</strong> the first way and <strong>remove</strong> " 133 + "the other ways that are members of the same relation: the combined way will " 134 + "take the place of the original way in the relation." 135 + "</html>")); 136 invalidate(); 137 } 138 139 /** 140 * Initializes for node merging. 141 */ 142 public void initForNodeMerging() { 143 lblHeader.setText(tr("<html>The merged nodes are members in one or more relations. " 144 + "Please decide whether you want to <strong>keep</strong> these memberships " 145 + "for the target node or whether you want to <strong>remove</strong> them.<br>" 146 + "The default is to <strong>keep</strong> the first node and <strong>remove</strong> " 147 + "the other nodes that are members of the same relation: the target node will " 148 + "take the place of the original node in the relation." 149 + "</html>")); 150 invalidate(); 151 } 152 153 class ApplyRoleAction extends AbstractAction { 154 ApplyRoleAction() { 155 putValue(NAME, tr("Apply")); 156 putValue(SMALL_ICON, ImageProvider.get("ok")); 157 putValue(SHORT_DESCRIPTION, tr("Apply this role to all members")); 158 } 159 160 @Override 161 public void actionPerformed(ActionEvent e) { 162 model.applyRole(tfRole.getText()); 163 } 164 } 165 166 class ToggleTagRelationsAction implements ChangeListener { 167 @Override 168 public void stateChanged(ChangeEvent e) { 169 ButtonModel buttonModel = ((AbstractButton) e.getSource()).getModel(); 170 tfKey.setEnabled(buttonModel.isSelected()); 171 tfValue.setEnabled(buttonModel.isSelected()); 172 tfKey.setBackground(buttonModel.isSelected() ? UIManager.getColor("TextField.background") : UIManager 173 .getColor("Panel.background")); 174 tfValue.setBackground(buttonModel.isSelected() ? UIManager.getColor("TextField.background") : UIManager 175 .getColor("Panel.background")); 176 } 177 } 178 179 public RelationMemberConflictResolverModel getModel() { 180 return model; 181 } 182 183 public Command buildTagApplyCommands(Collection<? extends OsmPrimitive> primitives) { 184 if (!cbTagRelations.isSelected()) 185 return null; 186 if (tfKey.getText().trim().isEmpty()) 187 return null; 188 if (tfValue.getText().trim().isEmpty()) 189 return null; 190 if (primitives == null || primitives.isEmpty()) 191 return null; 192 return new ChangePropertyCommand(primitives, Tag.removeWhiteSpaces(tfKey.getText()), Tag.removeWhiteSpaces(tfValue.getText())); 193 } 194 195 public void prepareForEditing() { 196 AutoCompletionList acList = new AutoCompletionList(); 197 Main.main.getEditLayer().data.getAutoCompletionManager().populateWithMemberRoles(acList); 198 tfRole.setAutoCompletionList(acList); 199 AutoCompletingTextField editor = (AutoCompletingTextField) tblResolver.getColumnModel().getColumn(2).getCellEditor(); 200 if (editor != null) { 201 editor.setAutoCompletionList(acList); 202 } 203 AutoCompletionList acList2 = new AutoCompletionList(); 204 Main.main.getEditLayer().data.getAutoCompletionManager().populateWithKeys(acList2); 205 tfKey.setAutoCompletionList(acList2); 206 } 207}