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 }