001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.BorderLayout; 008import java.awt.Component; 009import java.awt.Dimension; 010import java.awt.FlowLayout; 011import java.awt.event.ActionEvent; 012import java.beans.PropertyChangeEvent; 013import java.beans.PropertyChangeListener; 014 015import javax.swing.AbstractAction; 016import javax.swing.Action; 017import javax.swing.BorderFactory; 018import javax.swing.JButton; 019import javax.swing.JDialog; 020import javax.swing.JOptionPane; 021import javax.swing.JPanel; 022import javax.swing.WindowConstants; 023 024import org.openstreetmap.josm.Main; 025import org.openstreetmap.josm.command.Command; 026import org.openstreetmap.josm.data.osm.OsmPrimitive; 027import org.openstreetmap.josm.gui.DefaultNameFormatter; 028import org.openstreetmap.josm.gui.conflict.pair.ConflictResolver; 029import org.openstreetmap.josm.gui.help.HelpBrowser; 030import org.openstreetmap.josm.gui.help.HelpUtil; 031import org.openstreetmap.josm.tools.ImageProvider; 032import org.openstreetmap.josm.tools.WindowGeometry; 033 034/** 035 * This is an extended dialog for resolving conflict between {@link OsmPrimitive}s. 036 * 037 */ 038public class ConflictResolutionDialog extends JDialog implements PropertyChangeListener { 039 /** the conflict resolver component */ 040 private ConflictResolver resolver; 041 042 private ApplyResolutionAction applyResolutionAction; 043 044 @Override 045 public void removeNotify() { 046 super.removeNotify(); 047 unregisterListeners(); 048 } 049 050 @Override 051 public void setVisible(boolean isVisible) { 052 String geom = getClass().getName() + ".geometry"; 053 if (isVisible){ 054 toFront(); 055 new WindowGeometry(geom, WindowGeometry.centerInWindow(Main.parent, 056 new Dimension(600, 400))).applySafe(this); 057 } else { 058 if (isShowing()) { // Avoid IllegalComponentStateException like in #8775 059 new WindowGeometry(this).remember(geom); 060 } 061 unregisterListeners(); 062 } 063 super.setVisible(isVisible); 064 } 065 066 private void closeDialog() { 067 setVisible(false); 068 dispose(); 069 } 070 071 /** 072 * builds the sub panel with the control buttons 073 * 074 * @return the panel 075 */ 076 protected JPanel buildButtonRow() { 077 JPanel pnl = new JPanel(); 078 pnl.setLayout(new FlowLayout(FlowLayout.CENTER)); 079 080 applyResolutionAction = new ApplyResolutionAction(); 081 JButton btn = new JButton(applyResolutionAction); 082 btn.setName("button.apply"); 083 pnl.add(btn); 084 085 btn = new JButton(new CancelAction()); 086 btn.setName("button.cancel"); 087 pnl.add(btn); 088 089 btn = new JButton(new HelpAction()); 090 btn.setName("button.help"); 091 pnl.add(btn); 092 093 pnl.setBorder(BorderFactory.createLoweredBevelBorder()); 094 return pnl; 095 } 096 097 private void registerListeners() { 098 resolver.addPropertyChangeListener(applyResolutionAction); 099 } 100 101 private void unregisterListeners() { 102 resolver.removePropertyChangeListener(applyResolutionAction); 103 resolver.unregisterListeners(); 104 } 105 106 /** 107 * builds the GUI 108 */ 109 protected void build() { 110 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 111 updateTitle(); 112 getContentPane().setLayout(new BorderLayout()); 113 114 resolver = new ConflictResolver(); 115 resolver.setName("panel.conflictresolver"); 116 getContentPane().add(resolver, BorderLayout.CENTER); 117 getContentPane().add(buildButtonRow(), BorderLayout.SOUTH); 118 119 resolver.addPropertyChangeListener(this); 120 HelpUtil.setHelpContext(this.getRootPane(), ht("Dialog/Conflict")); 121 122 registerListeners(); 123 } 124 125 public ConflictResolutionDialog(Component parent) { 126 super(JOptionPane.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL); 127 build(); 128 } 129 130 public ConflictResolver getConflictResolver() { 131 return resolver; 132 } 133 134 /** 135 * Action for canceling conflict resolution 136 */ 137 class CancelAction extends AbstractAction { 138 public CancelAction() { 139 putValue(Action.SHORT_DESCRIPTION, tr("Cancel conflict resolution and close the dialog")); 140 putValue(Action.NAME, tr("Cancel")); 141 putValue(Action.SMALL_ICON, ImageProvider.get("", "cancel")); 142 setEnabled(true); 143 } 144 145 @Override 146 public void actionPerformed(ActionEvent arg0) { 147 closeDialog(); 148 } 149 } 150 151 /** 152 * Action for canceling conflict resolution 153 */ 154 static class HelpAction extends AbstractAction { 155 public HelpAction() { 156 putValue(Action.SHORT_DESCRIPTION, tr("Show help information")); 157 putValue(Action.NAME, tr("Help")); 158 putValue(Action.SMALL_ICON, ImageProvider.get("help")); 159 setEnabled(true); 160 } 161 162 @Override 163 public void actionPerformed(ActionEvent arg0) { 164 HelpBrowser.setUrlForHelpTopic(ht("/Dialog/Conflict")); 165 } 166 } 167 168 /** 169 * Action for applying resolved differences in a conflict 170 * 171 */ 172 class ApplyResolutionAction extends AbstractAction implements PropertyChangeListener { 173 public ApplyResolutionAction() { 174 putValue(Action.SHORT_DESCRIPTION, tr("Apply resolved conflicts and close the dialog")); 175 putValue(Action.NAME, tr("Apply Resolution")); 176 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs", "conflict")); 177 updateEnabledState(); 178 } 179 180 protected void updateEnabledState() { 181 setEnabled(resolver.isResolvedCompletely()); 182 } 183 184 @Override 185 public void actionPerformed(ActionEvent arg0) { 186 if (! resolver.isResolvedCompletely()) { 187 Object[] options = { 188 tr("Close anyway"), 189 tr("Continue resolving")}; 190 int ret = JOptionPane.showOptionDialog(Main.parent, 191 tr("<html>You did not finish to merge the differences in this conflict.<br>" 192 + "Conflict resolutions will not be applied unless all differences<br>" 193 + "are resolved.<br>" 194 + "Click <strong>{0}</strong> to close anyway.<strong> Already<br>" 195 + "resolved differences will not be applied.</strong><br>" 196 + "Click <strong>{1}</strong> to return to resolving conflicts.</html>" 197 , options[0].toString(), options[1].toString() 198 ), 199 tr("Conflict not resolved completely"), 200 JOptionPane.YES_NO_OPTION, 201 JOptionPane.WARNING_MESSAGE, 202 null, 203 options, 204 options[1] 205 ); 206 switch(ret) { 207 case JOptionPane.YES_OPTION: 208 closeDialog(); 209 break; 210 default: 211 return; 212 } 213 } 214 Command cmd = resolver.buildResolveCommand(); 215 Main.main.undoRedo.add(cmd); 216 closeDialog(); 217 } 218 219 @Override 220 public void propertyChange(PropertyChangeEvent evt) { 221 if (evt.getPropertyName().equals(ConflictResolver.RESOLVED_COMPLETELY_PROP)) { 222 updateEnabledState(); 223 } 224 } 225 } 226 227 protected void updateTitle() { 228 updateTitle(null); 229 } 230 231 protected void updateTitle(OsmPrimitive my) { 232 if (my == null) { 233 setTitle(tr("Resolve conflicts")); 234 } else { 235 setTitle(tr("Resolve conflicts for ''{0}''", my.getDisplayName(DefaultNameFormatter.getInstance()))); 236 } 237 } 238 239 @Override 240 public void propertyChange(PropertyChangeEvent evt) { 241 if (evt.getPropertyName().equals(ConflictResolver.MY_PRIMITIVE_PROP)) { 242 updateTitle((OsmPrimitive)evt.getNewValue()); 243 } 244 } 245}