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

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

move UserCancelException class in tools package and use it where applicable

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