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

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

code refactoring to ease creation of unit tests by avoiding as much as possible HeadlessException

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