source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java@ 10611

Last change on this file since 10611 was 10611, checked in by Don-vip, 8 years ago

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

  • Property svn:eol-style set to native
File size: 24.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.tags;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.BorderLayout;
9import java.awt.Component;
10import java.awt.Dimension;
11import java.awt.FlowLayout;
12import java.awt.GraphicsEnvironment;
13import java.awt.event.ActionEvent;
14import java.awt.event.HierarchyBoundsListener;
15import java.awt.event.HierarchyEvent;
16import java.awt.event.WindowAdapter;
17import java.awt.event.WindowEvent;
18import java.beans.PropertyChangeEvent;
19import java.beans.PropertyChangeListener;
20import java.util.Collection;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Set;
24
25import javax.swing.AbstractAction;
26import javax.swing.Action;
27import javax.swing.JButton;
28import javax.swing.JDialog;
29import javax.swing.JLabel;
30import javax.swing.JOptionPane;
31import javax.swing.JPanel;
32import javax.swing.JSplitPane;
33
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.actions.ExpertToggleAction;
36import org.openstreetmap.josm.command.ChangePropertyCommand;
37import org.openstreetmap.josm.command.Command;
38import org.openstreetmap.josm.data.osm.Node;
39import org.openstreetmap.josm.data.osm.OsmPrimitive;
40import org.openstreetmap.josm.data.osm.Relation;
41import org.openstreetmap.josm.data.osm.TagCollection;
42import org.openstreetmap.josm.data.osm.Way;
43import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
44import org.openstreetmap.josm.gui.DefaultNameFormatter;
45import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
46import org.openstreetmap.josm.gui.help.HelpUtil;
47import org.openstreetmap.josm.gui.util.GuiHelper;
48import org.openstreetmap.josm.tools.CheckParameterUtil;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.UserCancelException;
51import org.openstreetmap.josm.tools.Utils;
52import org.openstreetmap.josm.tools.Utils.Function;
53import org.openstreetmap.josm.tools.WindowGeometry;
54
55/**
56 * This dialog helps to resolve conflicts occurring when ways are combined or
57 * nodes are merged.
58 *
59 * Usage: {@link #launchIfNecessary} followed by {@link #buildResolutionCommands}.
60 *
61 * Prior to {@link #launchIfNecessary}, the following usage sequence was needed:
62 *
63 * There is a singleton instance of this dialog which can be retrieved using
64 * {@link #getInstance()}.
65 *
66 * The dialog uses two models: one for resolving tag conflicts, the other
67 * for resolving conflicts in relation memberships. For both models there are accessors,
68 * i.e {@link #getTagConflictResolverModel()} and {@link #getRelationMemberConflictResolverModel()}.
69 *
70 * Models have to be <strong>populated</strong> before the dialog is launched. Example:
71 * <pre>
72 * CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
73 * dialog.getTagConflictResolverModel().populate(aTagCollection);
74 * dialog.getRelationMemberConflictResolverModel().populate(aRelationLinkCollection);
75 * dialog.prepareDefaultDecisions();
76 * </pre>
77 *
78 * You should also set the target primitive which other primitives (ways or nodes) are
79 * merged to, see {@link #setTargetPrimitive(OsmPrimitive)}.
80 *
81 * After the dialog is closed use {@link #isApplied()} to check whether the dialog has been
82 * applied. If it was applied you may build a collection of {@link Command} objects
83 * which reflect the conflict resolution decisions the user made in the dialog:
84 * see {@link #buildResolutionCommands()}
85 */
86public class CombinePrimitiveResolverDialog extends JDialog {
87
88 /** the unique instance of the dialog */
89 private static CombinePrimitiveResolverDialog instance;
90
91 /**
92 * Replies the unique instance of the dialog
93 *
94 * @return the unique instance of the dialog
95 * @deprecated use {@link #launchIfNecessary} instead.
96 */
97 @Deprecated
98 public static synchronized CombinePrimitiveResolverDialog getInstance() {
99 if (instance == null) {
100 GuiHelper.runInEDTAndWait(() -> instance = new CombinePrimitiveResolverDialog(Main.parent));
101 }
102 return instance;
103 }
104
105 private AutoAdjustingSplitPane spTagConflictTypes;
106 private TagConflictResolver pnlTagConflictResolver;
107 protected RelationMemberConflictResolver pnlRelationMemberConflictResolver;
108 private boolean applied;
109 private JPanel pnlButtons;
110 protected transient OsmPrimitive targetPrimitive;
111
112 /** the private help action */
113 private ContextSensitiveHelpAction helpAction;
114 /** the apply button */
115 private JButton btnApply;
116
117 /**
118 * Replies the target primitive the collection of primitives is merged
119 * or combined to.
120 *
121 * @return the target primitive
122 */
123 public OsmPrimitive getTargetPrimitmive() {
124 return targetPrimitive;
125 }
126
127 /**
128 * Sets the primitive the collection of primitives is merged or combined to.
129 *
130 * @param primitive the target primitive
131 */
132 public void setTargetPrimitive(final OsmPrimitive primitive) {
133 this.targetPrimitive = primitive;
134 GuiHelper.runInEDTAndWait(() -> {
135 updateTitle();
136 if (primitive instanceof Way) {
137 pnlRelationMemberConflictResolver.initForWayCombining();
138 } else if (primitive instanceof Node) {
139 pnlRelationMemberConflictResolver.initForNodeMerging();
140 }
141 });
142 }
143
144 protected void updateTitle() {
145 if (targetPrimitive == null) {
146 setTitle(tr("Conflicts when combining primitives"));
147 return;
148 }
149 if (targetPrimitive instanceof Way) {
150 setTitle(tr("Conflicts when combining ways - combined way is ''{0}''", targetPrimitive
151 .getDisplayName(DefaultNameFormatter.getInstance())));
152 helpAction.setHelpTopic(ht("/Action/CombineWay#ResolvingConflicts"));
153 getRootPane().putClientProperty("help", ht("/Action/CombineWay#ResolvingConflicts"));
154 } else if (targetPrimitive instanceof Node) {
155 setTitle(tr("Conflicts when merging nodes - target node is ''{0}''", targetPrimitive
156 .getDisplayName(DefaultNameFormatter.getInstance())));
157 helpAction.setHelpTopic(ht("/Action/MergeNodes#ResolvingConflicts"));
158 getRootPane().putClientProperty("help", ht("/Action/MergeNodes#ResolvingConflicts"));
159 }
160 }
161
162 protected final void build() {
163 getContentPane().setLayout(new BorderLayout());
164 updateTitle();
165 spTagConflictTypes = new AutoAdjustingSplitPane(JSplitPane.VERTICAL_SPLIT);
166 spTagConflictTypes.setTopComponent(buildTagConflictResolverPanel());
167 spTagConflictTypes.setBottomComponent(buildRelationMemberConflictResolverPanel());
168 pnlButtons = buildButtonPanel();
169 getContentPane().add(pnlButtons, BorderLayout.SOUTH);
170 addWindowListener(new AdjustDividerLocationAction());
171 HelpUtil.setHelpContext(getRootPane(), ht("/"));
172 }
173
174 protected JPanel buildTagConflictResolverPanel() {
175 pnlTagConflictResolver = new TagConflictResolver();
176 return pnlTagConflictResolver;
177 }
178
179 protected JPanel buildRelationMemberConflictResolverPanel() {
180 pnlRelationMemberConflictResolver = new RelationMemberConflictResolver(new RelationMemberConflictResolverModel());
181 return pnlRelationMemberConflictResolver;
182 }
183
184 protected ApplyAction buildApplyAction() {
185 return new ApplyAction();
186 }
187
188 protected JPanel buildButtonPanel() {
189 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
190
191 // -- apply button
192 ApplyAction applyAction = buildApplyAction();
193 pnlTagConflictResolver.getModel().addPropertyChangeListener(applyAction);
194 pnlRelationMemberConflictResolver.getModel().addPropertyChangeListener(applyAction);
195 btnApply = new JButton(applyAction);
196 btnApply.setFocusable(true);
197 pnl.add(btnApply);
198
199 // -- cancel button
200 CancelAction cancelAction = new CancelAction();
201 pnl.add(new JButton(cancelAction));
202
203 // -- help button
204 helpAction = new ContextSensitiveHelpAction();
205 pnl.add(new JButton(helpAction));
206
207 return pnl;
208 }
209
210 /**
211 * Constructs a new {@code CombinePrimitiveResolverDialog}.
212 * @param parent The parent component in which this dialog will be displayed.
213 */
214 public CombinePrimitiveResolverDialog(Component parent) {
215 super(GuiHelper.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
216 build();
217 }
218
219 /**
220 * Replies the tag conflict resolver model.
221 * @return The tag conflict resolver model.
222 */
223 public TagConflictResolverModel getTagConflictResolverModel() {
224 return pnlTagConflictResolver.getModel();
225 }
226
227 /**
228 * Replies the relation membership conflict resolver model.
229 * @return The relation membership conflict resolver model.
230 */
231 public RelationMemberConflictResolverModel getRelationMemberConflictResolverModel() {
232 return pnlRelationMemberConflictResolver.getModel();
233 }
234
235 /**
236 * Replies true if all tag and relation member conflicts have been decided.
237 *
238 * @return true if all tag and relation member conflicts have been decided; false otherwise
239 */
240 public boolean isResolvedCompletely() {
241 return getTagConflictResolverModel().isResolvedCompletely()
242 && getRelationMemberConflictResolverModel().isResolvedCompletely();
243 }
244
245 protected List<Command> buildTagChangeCommand(OsmPrimitive primitive, TagCollection tc) {
246 List<Command> cmds = new LinkedList<>();
247 for (String key : tc.getKeys()) {
248 if (tc.hasUniqueEmptyValue(key)) {
249 if (primitive.get(key) != null) {
250 cmds.add(new ChangePropertyCommand(primitive, key, null));
251 }
252 } else {
253 String value = tc.getJoinedValues(key);
254 if (!value.equals(primitive.get(key))) {
255 cmds.add(new ChangePropertyCommand(primitive, key, value));
256 }
257 }
258 }
259 return cmds;
260 }
261
262 /**
263 * Replies the list of {@link Command commands} needed to apply resolution choices.
264 * @return The list of {@link Command commands} needed to apply resolution choices.
265 */
266 public List<Command> buildResolutionCommands() {
267 List<Command> cmds = new LinkedList<>();
268
269 TagCollection allResolutions = getTagConflictResolverModel().getAllResolutions();
270 if (!allResolutions.isEmpty()) {
271 cmds.addAll(buildTagChangeCommand(targetPrimitive, allResolutions));
272 }
273 for (String p : OsmPrimitive.getDiscardableKeys()) {
274 if (targetPrimitive.get(p) != null) {
275 cmds.add(new ChangePropertyCommand(targetPrimitive, p, null));
276 }
277 }
278
279 if (getRelationMemberConflictResolverModel().getNumDecisions() > 0) {
280 cmds.addAll(getRelationMemberConflictResolverModel().buildResolutionCommands(targetPrimitive));
281 }
282
283 Command cmd = pnlRelationMemberConflictResolver.buildTagApplyCommands(getRelationMemberConflictResolverModel()
284 .getModifiedRelations(targetPrimitive));
285 if (cmd != null) {
286 cmds.add(cmd);
287 }
288 return cmds;
289 }
290
291 /**
292 * Prepares the default decisions for populated tag and relation membership conflicts.
293 */
294 public void prepareDefaultDecisions() {
295 getTagConflictResolverModel().prepareDefaultTagDecisions();
296 getRelationMemberConflictResolverModel().prepareDefaultRelationDecisions();
297 }
298
299 protected JPanel buildEmptyConflictsPanel() {
300 JPanel pnl = new JPanel(new BorderLayout());
301 pnl.add(new JLabel(tr("No conflicts to resolve")));
302 return pnl;
303 }
304
305 protected void prepareGUIBeforeConflictResolutionStarts() {
306 RelationMemberConflictResolverModel relModel = getRelationMemberConflictResolverModel();
307 TagConflictResolverModel tagModel = getTagConflictResolverModel();
308 getContentPane().removeAll();
309
310 if (relModel.getNumDecisions() > 0 && tagModel.getNumDecisions() > 0) {
311 // display both, the dialog for resolving relation conflicts and for resolving tag conflicts
312 spTagConflictTypes.setTopComponent(pnlTagConflictResolver);
313 spTagConflictTypes.setBottomComponent(pnlRelationMemberConflictResolver);
314 getContentPane().add(spTagConflictTypes, BorderLayout.CENTER);
315 } else if (relModel.getNumDecisions() > 0) {
316 // relation conflicts only
317 getContentPane().add(pnlRelationMemberConflictResolver, BorderLayout.CENTER);
318 } else if (tagModel.getNumDecisions() > 0) {
319 // tag conflicts only
320 getContentPane().add(pnlTagConflictResolver, BorderLayout.CENTER);
321 } else {
322 getContentPane().add(buildEmptyConflictsPanel(), BorderLayout.CENTER);
323 }
324
325 getContentPane().add(pnlButtons, BorderLayout.SOUTH);
326 validate();
327 adjustDividerLocation();
328 pnlRelationMemberConflictResolver.prepareForEditing();
329 }
330
331 protected void setApplied(boolean applied) {
332 this.applied = applied;
333 }
334
335 /**
336 * Determines if this dialog has been closed with "Apply".
337 * @return true if this dialog has been closed with "Apply", false otherwise.
338 */
339 public boolean isApplied() {
340 return applied;
341 }
342
343 @Override
344 public void setVisible(boolean visible) {
345 if (visible) {
346 prepareGUIBeforeConflictResolutionStarts();
347 setMinimumSize(new Dimension(400, 400));
348 new WindowGeometry(getClass().getName() + ".geometry", WindowGeometry.centerInWindow(Main.parent,
349 new Dimension(800, 600))).applySafe(this);
350 setApplied(false);
351 btnApply.requestFocusInWindow();
352 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
353 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
354 }
355 super.setVisible(visible);
356 }
357
358 class CancelAction extends AbstractAction {
359
360 CancelAction() {
361 putValue(Action.SHORT_DESCRIPTION, tr("Cancel conflict resolution"));
362 putValue(Action.NAME, tr("Cancel"));
363 new ImageProvider("cancel").getResource().attachImageIcon(this);
364 setEnabled(true);
365 }
366
367 @Override
368 public void actionPerformed(ActionEvent arg0) {
369 setVisible(false);
370 }
371 }
372
373 protected class ApplyAction extends AbstractAction implements PropertyChangeListener {
374
375 public ApplyAction() {
376 putValue(Action.SHORT_DESCRIPTION, tr("Apply resolved conflicts"));
377 putValue(Action.NAME, tr("Apply"));
378 new ImageProvider("ok").getResource().attachImageIcon(this);
379 updateEnabledState();
380 }
381
382 @Override
383 public void actionPerformed(ActionEvent arg0) {
384 setApplied(true);
385 setVisible(false);
386 pnlTagConflictResolver.rememberPreferences();
387 }
388
389 protected final void updateEnabledState() {
390 setEnabled(pnlTagConflictResolver.getModel().getNumConflicts() == 0
391 && pnlRelationMemberConflictResolver.getModel().getNumConflicts() == 0);
392 }
393
394 @Override
395 public void propertyChange(PropertyChangeEvent evt) {
396 if (evt.getPropertyName().equals(TagConflictResolverModel.NUM_CONFLICTS_PROP)) {
397 updateEnabledState();
398 }
399 if (evt.getPropertyName().equals(RelationMemberConflictResolverModel.NUM_CONFLICTS_PROP)) {
400 updateEnabledState();
401 }
402 }
403 }
404
405 private void adjustDividerLocation() {
406 int numTagDecisions = getTagConflictResolverModel().getNumDecisions();
407 int numRelationDecisions = getRelationMemberConflictResolverModel().getNumDecisions();
408 if (numTagDecisions > 0 && numRelationDecisions > 0) {
409 double nTop = 1.0 + numTagDecisions;
410 double nBottom = 2.5 + numRelationDecisions;
411 spTagConflictTypes.setDividerLocation(nTop/(nTop+nBottom));
412 }
413 }
414
415 class AdjustDividerLocationAction extends WindowAdapter {
416 @Override
417 public void windowOpened(WindowEvent e) {
418 adjustDividerLocation();
419 }
420 }
421
422 static class AutoAdjustingSplitPane extends JSplitPane implements PropertyChangeListener, HierarchyBoundsListener {
423 private double dividerLocation;
424
425 AutoAdjustingSplitPane(int newOrientation) {
426 super(newOrientation);
427 addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, this);
428 addHierarchyBoundsListener(this);
429 }
430
431 @Override
432 public void ancestorResized(HierarchyEvent e) {
433 setDividerLocation((int) (dividerLocation * getHeight()));
434 }
435
436 @Override
437 public void ancestorMoved(HierarchyEvent e) {
438 // do nothing
439 }
440
441 @Override
442 public void propertyChange(PropertyChangeEvent evt) {
443 if (JSplitPane.DIVIDER_LOCATION_PROPERTY.equals(evt.getPropertyName())) {
444 int newVal = (Integer) evt.getNewValue();
445 if (getHeight() != 0) {
446 dividerLocation = (double) newVal / (double) getHeight();
447 }
448 }
449 }
450 }
451
452 /**
453 * Replies the list of {@link Command commands} needed to resolve specified conflicts,
454 * by displaying if necessary a {@link CombinePrimitiveResolverDialog} to the user.
455 * This dialog will allow the user to choose conflict resolution actions.
456 *
457 * Non-expert users are informed first of the meaning of these operations, allowing them to cancel.
458 *
459 * @param tagsOfPrimitives The tag collection of the primitives to be combined.
460 * Should generally be equal to {@code TagCollection.unionOfAllPrimitives(primitives)}
461 * @param primitives The primitives to be combined
462 * @param targetPrimitives The primitives the collection of primitives are merged or combined to.
463 * @return The list of {@link Command commands} needed to apply resolution actions.
464 * @throws UserCancelException If the user cancelled a dialog.
465 */
466 public static List<Command> launchIfNecessary(
467 final TagCollection tagsOfPrimitives,
468 final Collection<? extends OsmPrimitive> primitives,
469 final Collection<? extends OsmPrimitive> targetPrimitives) throws UserCancelException {
470
471 CheckParameterUtil.ensureParameterNotNull(tagsOfPrimitives, "tagsOfPrimitives");
472 CheckParameterUtil.ensureParameterNotNull(primitives, "primitives");
473 CheckParameterUtil.ensureParameterNotNull(targetPrimitives, "targetPrimitives");
474
475 final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives);
476 TagConflictResolutionUtil.combineTigerTags(completeWayTags);
477 TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives);
478 final TagCollection tagsToEdit = new TagCollection(completeWayTags);
479 TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
480
481 final Set<Relation> parentRelations = OsmPrimitive.getParentRelations(primitives);
482
483 // Show information dialogs about conflicts to non-experts
484 if (!ExpertToggleAction.isExpert()) {
485 // Tag conflicts
486 if (!completeWayTags.isApplicableToPrimitive()) {
487 informAboutTagConflicts(primitives, completeWayTags);
488 }
489 // Relation membership conflicts
490 if (!parentRelations.isEmpty()) {
491 informAboutRelationMembershipConflicts(primitives, parentRelations);
492 }
493 }
494
495 List<Command> cmds = new LinkedList<>();
496
497 if (!GraphicsEnvironment.isHeadless()) {
498 // Build conflict resolution dialog
499 final CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
500
501 dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues());
502 dialog.getRelationMemberConflictResolverModel().populate(parentRelations, primitives);
503 dialog.prepareDefaultDecisions();
504
505 // Ensure a proper title is displayed instead of a previous target (fix #7925)
506 if (targetPrimitives.size() == 1) {
507 dialog.setTargetPrimitive(targetPrimitives.iterator().next());
508 } else {
509 dialog.setTargetPrimitive(null);
510 }
511
512 // Resolve tag conflicts if necessary
513 if (!dialog.isResolvedCompletely()) {
514 dialog.setVisible(true);
515 if (!dialog.isApplied()) {
516 throw new UserCancelException();
517 }
518 }
519 for (OsmPrimitive i : targetPrimitives) {
520 dialog.setTargetPrimitive(i);
521 cmds.addAll(dialog.buildResolutionCommands());
522 }
523 }
524 return cmds;
525 }
526
527 /**
528 * Inform a non-expert user about what relation membership conflict resolution means.
529 * @param primitives The primitives to be combined
530 * @param parentRelations The parent relations of the primitives
531 * @throws UserCancelException If the user cancels the dialog.
532 */
533 protected static void informAboutRelationMembershipConflicts(
534 final Collection<? extends OsmPrimitive> primitives,
535 final Set<Relation> parentRelations) throws UserCancelException {
536 /* I18n: object count < 2 is not possible */
537 String msg = trn("You are about to combine {1} object, "
538 + "which is part of {0} relation:<br/>{2}"
539 + "Combining these objects may break this relation. If you are unsure, please cancel this operation.<br/>"
540 + "If you want to continue, you are shown a dialog to decide how to adapt the relation.<br/><br/>"
541 + "Do you want to continue?",
542 "You are about to combine {1} objects, "
543 + "which are part of {0} relations:<br/>{2}"
544 + "Combining these objects may break these relations. If you are unsure, please cancel this operation.<br/>"
545 + "If you want to continue, you are shown a dialog to decide how to adapt the relations.<br/><br/>"
546 + "Do you want to continue?",
547 parentRelations.size(), parentRelations.size(), primitives.size(),
548 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(parentRelations, 20));
549
550 if (!ConditionalOptionPaneUtil.showConfirmationDialog(
551 "combine_tags",
552 Main.parent,
553 "<html>" + msg + "</html>",
554 tr("Combine confirmation"),
555 JOptionPane.YES_NO_OPTION,
556 JOptionPane.QUESTION_MESSAGE,
557 JOptionPane.YES_OPTION)) {
558 throw new UserCancelException();
559 }
560 }
561
562 /**
563 * Inform a non-expert user about what tag conflict resolution means.
564 * @param primitives The primitives to be combined
565 * @param normalizedTags The normalized tag collection of the primitives to be combined
566 * @throws UserCancelException If the user cancels the dialog.
567 */
568 protected static void informAboutTagConflicts(
569 final Collection<? extends OsmPrimitive> primitives,
570 final TagCollection normalizedTags) throws UserCancelException {
571 String conflicts = Utils.joinAsHtmlUnorderedList(Utils.transform(normalizedTags.getKeysWithMultipleValues(),
572 (Function<String, String>) key -> tr("{0} ({1})", key, Utils.join(tr(", "), Utils.transform(normalizedTags.getValues(key),
573 (Function<String, String>) x -> x == null || x.isEmpty() ? tr("<i>missing</i>") : x)))));
574 String msg = /* for correct i18n of plural forms - see #9110 */ trn("You are about to combine {0} objects, "
575 + "but the following tags are used conflictingly:<br/>{1}"
576 + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
577 + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
578 + "Do you want to continue?", "You are about to combine {0} objects, "
579 + "but the following tags are used conflictingly:<br/>{1}"
580 + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
581 + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
582 + "Do you want to continue?",
583 primitives.size(), primitives.size(), conflicts);
584
585 if (!ConditionalOptionPaneUtil.showConfirmationDialog(
586 "combine_tags",
587 Main.parent,
588 "<html>" + msg + "</html>",
589 tr("Combine confirmation"),
590 JOptionPane.YES_NO_OPTION,
591 JOptionPane.QUESTION_MESSAGE,
592 JOptionPane.YES_OPTION)) {
593 throw new UserCancelException();
594 }
595 }
596}
Note: See TracBrowser for help on using the repository browser.