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    }