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