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 RelationMemberConflictResolverModel model; 047 private RelationMemberConflictResolverTable tblResolver; 048 private JMultilineLabel lblHeader; 049 050 protected final void build() { 051 setLayout(new GridBagLayout()); 052 JPanel pnl = new JPanel(); 053 pnl.setLayout(new BorderLayout()); 054 pnl.add(lblHeader = new JMultilineLabel("")); 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 model = new RelationMemberConflictResolverModel(); 062 063 gc.gridy = 1; 064 gc.weighty = 1.0; 065 gc.fill = GridBagConstraints.BOTH; 066 gc.insets = new Insets(0,0,0,0); 067 add(new JScrollPane(tblResolver = new RelationMemberConflictResolverTable(model)), gc); 068 pnl = new JPanel(); 069 pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS)); 070 pnl.add(buildRoleEditingPanel()); 071 pnl.add(buildTagRelationsPanel()); 072 gc.gridy = 2; 073 gc.weighty = 0.0; 074 gc.fill = GridBagConstraints.HORIZONTAL; 075 add(pnl,gc); 076 } 077 078 protected JPanel buildRoleEditingPanel() { 079 JPanel pnl = new JPanel(); 080 pnl.setLayout(new FlowLayout(FlowLayout.LEFT)); 081 pnl.add(new JLabel(tr("Role:"))); 082 pnl.add(tfRole = new AutoCompletingTextField(10)); 083 tfRole.setToolTipText(tr("Enter a role for all relation memberships")); 084 pnl.add(new JButton(new ApplyRoleAction())); 085 tfRole.addActionListener(new ApplyRoleAction()); 086 tfRole.addFocusListener( 087 new FocusAdapter() { 088 @Override 089 public void focusGained(FocusEvent e) { 090 tfRole.selectAll(); 091 } 092 } 093 ); 094 return pnl; 095 } 096 097 protected JPanel buildTagRelationsPanel() { 098 JPanel pnl = new JPanel(); 099 pnl.setLayout(new FlowLayout(FlowLayout.LEFT)); 100 cbTagRelations = new JCheckBox(tr("Tag modified relations with ")); 101 cbTagRelations.addChangeListener(new ToggleTagRelationsAction()); 102 cbTagRelations.setToolTipText( 103 tr("<html>Select to enable entering a tag which will be applied<br>" 104 + "to all modified relations.</html>")); 105 pnl.add(cbTagRelations); 106 pnl.add(new JLabel(trc("tag", "Key:"))); 107 pnl.add(tfKey = new AutoCompletingTextField(10)); 108 tfKey.setToolTipText(tr("<html>Enter a tag key, e.g. <strong><tt>fixme</tt></strong></html>")); 109 pnl.add(new JLabel(tr("Value:"))); 110 pnl.add(tfValue = new AutoCompletingTextField(10)); 111 tfValue.setToolTipText(tr("<html>Enter a tag value, e.g. <strong><tt>check members</tt></strong></html>")); 112 cbTagRelations.setSelected(false); 113 tfKey.setEnabled(false); 114 tfValue.setEnabled(false); 115 return pnl; 116 } 117 118 /** 119 * Constructs a new {@code RelationMemberConflictResolver}. 120 */ 121 public RelationMemberConflictResolver() { 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 public 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}