source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/tags/TagMerger.java@ 2512

Last change on this file since 2512 was 2512, checked in by stoecker, 14 years ago

i18n updated, fixed files to reduce problems when applying patches, fix #4017

File size: 12.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair.tags;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Adjustable;
7import java.awt.GridBagConstraints;
8import java.awt.GridBagLayout;
9import java.awt.Insets;
10import java.awt.event.ActionEvent;
11import java.awt.event.AdjustmentEvent;
12import java.awt.event.AdjustmentListener;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
15import java.util.ArrayList;
16
17import javax.swing.AbstractAction;
18import javax.swing.Action;
19import javax.swing.ImageIcon;
20import javax.swing.JButton;
21import javax.swing.JLabel;
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.JTable;
25import javax.swing.event.ListSelectionEvent;
26import javax.swing.event.ListSelectionListener;
27
28import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
29import org.openstreetmap.josm.tools.ImageProvider;
30/**
31 * UI component for resolving conflicts in the tag sets of two {@see OsmPrimitive}s.
32 *
33 */
34public class TagMerger extends JPanel {
35
36 private JTable mineTable;
37 private JTable mergedTable;
38 private JTable theirTable;
39 private final TagMergeModel model;
40 private JButton btnKeepMine;
41 private JButton btnKeepTheir;
42 AdjustmentSynchronizer adjustmentSynchronizer;
43
44 /**
45 * embeds table in a new {@see JScrollPane} and returns th scroll pane
46 *
47 * @param table the table
48 * @return the scroll pane embedding the table
49 */
50 protected JScrollPane embeddInScrollPane(JTable table) {
51 JScrollPane pane = new JScrollPane(table);
52 pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
53 pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
54
55 adjustmentSynchronizer.synchronizeAdjustment(pane.getVerticalScrollBar());
56 return pane;
57 }
58
59 /**
60 * builds the table for my tag set (table already embedded in a scroll pane)
61 *
62 * @return the table (embedded in a scroll pane)
63 */
64 protected JScrollPane buildMineTagTable() {
65 mineTable = new JTable(
66 model,
67 new TagMergeColumnModel(
68 new MineTableCellRenderer()
69 )
70 );
71 mineTable.setName("table.my");
72 return embeddInScrollPane(mineTable);
73 }
74
75 /**
76 * builds the table for their tag set (table already embedded in a scroll pane)
77 *
78 * @return the table (embedded in a scroll pane)
79 */
80 protected JScrollPane buildTheirTable() {
81 theirTable = new JTable(
82 model,
83 new TagMergeColumnModel(
84 new TheirTableCellRenderer()
85 )
86 );
87 theirTable.setName("table.their");
88 return embeddInScrollPane(theirTable);
89 }
90
91 /**
92 * builds the table for the merged tag set (table already embedded in a scroll pane)
93 *
94 * @return the table (embedded in a scroll pane)
95 */
96
97 protected JScrollPane buildMergedTable() {
98 mergedTable = new JTable(
99 model,
100 new TagMergeColumnModel(
101 new MergedTableCellRenderer()
102 )
103 );
104 mergedTable.setName("table.merged");
105 return embeddInScrollPane(mergedTable);
106 }
107
108 /**
109 * build the user interface
110 */
111 protected void build() {
112 GridBagConstraints gc = new GridBagConstraints();
113 setLayout(new GridBagLayout());
114
115 adjustmentSynchronizer = new AdjustmentSynchronizer();
116
117 gc.gridx = 0;
118 gc.gridy = 0;
119 gc.gridwidth = 1;
120 gc.gridheight = 1;
121 gc.fill = GridBagConstraints.NONE;
122 gc.anchor = GridBagConstraints.CENTER;
123 gc.weightx = 0.0;
124 gc.weighty = 0.0;
125 gc.insets = new Insets(10,0,10,0);
126 JLabel lbl = new JLabel(tr("My version (local dataset)"));
127 add(lbl, gc);
128
129 gc.gridx = 2;
130 gc.gridy = 0;
131 gc.gridwidth = 1;
132 gc.gridheight = 1;
133 gc.fill = GridBagConstraints.NONE;
134 gc.anchor = GridBagConstraints.CENTER;
135 gc.weightx = 0.0;
136 gc.weighty = 0.0;
137 lbl = new JLabel(tr("Merged version"));
138 add(lbl, gc);
139
140 gc.gridx = 4;
141 gc.gridy = 0;
142 gc.gridwidth = 1;
143 gc.gridheight = 1;
144 gc.fill = GridBagConstraints.NONE;
145 gc.anchor = GridBagConstraints.CENTER;
146 gc.weightx = 0.0;
147 gc.weighty = 0.0;
148 gc.insets = new Insets(0,0,0,0);
149 lbl = new JLabel(tr("Their version (server dataset)"));
150 add(lbl, gc);
151
152 gc.gridx = 0;
153 gc.gridy = 1;
154 gc.gridwidth = 1;
155 gc.gridheight = 1;
156 gc.fill = GridBagConstraints.BOTH;
157 gc.anchor = GridBagConstraints.FIRST_LINE_START;
158 gc.weightx = 0.3;
159 gc.weighty = 1.0;
160 add(buildMineTagTable(), gc);
161
162 gc.gridx = 1;
163 gc.gridy = 1;
164 gc.gridwidth = 1;
165 gc.gridheight = 1;
166 gc.fill = GridBagConstraints.NONE;
167 gc.anchor = GridBagConstraints.CENTER;
168 gc.weightx = 0.0;
169 gc.weighty = 0.0;
170 KeepMineAction keepMineAction = new KeepMineAction();
171 mineTable.getSelectionModel().addListSelectionListener(keepMineAction);
172 btnKeepMine = new JButton(keepMineAction);
173 btnKeepMine.setName("button.keepmine");
174 add(btnKeepMine, gc);
175
176 gc.gridx = 2;
177 gc.gridy = 1;
178 gc.gridwidth = 1;
179 gc.gridheight = 1;
180 gc.fill = GridBagConstraints.BOTH;
181 gc.anchor = GridBagConstraints.FIRST_LINE_START;
182 gc.weightx = 0.3;
183 gc.weighty = 1.0;
184 add(buildMergedTable(), gc);
185
186 gc.gridx = 3;
187 gc.gridy = 1;
188 gc.gridwidth = 1;
189 gc.gridheight = 1;
190 gc.fill = GridBagConstraints.NONE;
191 gc.anchor = GridBagConstraints.CENTER;
192 gc.weightx = 0.0;
193 gc.weighty = 0.0;
194 KeepTheirAction keepTheirAction = new KeepTheirAction();
195 btnKeepTheir = new JButton(keepTheirAction);
196 btnKeepTheir.setName("button.keeptheir");
197 add(btnKeepTheir, gc);
198
199 gc.gridx = 4;
200 gc.gridy = 1;
201 gc.gridwidth = 1;
202 gc.gridheight = 1;
203 gc.fill = GridBagConstraints.BOTH;
204 gc.anchor = GridBagConstraints.FIRST_LINE_START;
205 gc.weightx = 0.3;
206 gc.weighty = 1.0;
207 add(buildTheirTable(), gc);
208 theirTable.getSelectionModel().addListSelectionListener(keepTheirAction);
209
210 DoubleClickAdapter dblClickAdapter = new DoubleClickAdapter();
211 mineTable.addMouseListener(dblClickAdapter);
212 theirTable.addMouseListener(dblClickAdapter);
213
214 gc.gridx = 2;
215 gc.gridy = 2;
216 gc.gridwidth = 1;
217 gc.gridheight = 1;
218 gc.fill = GridBagConstraints.NONE;
219 gc.anchor = GridBagConstraints.CENTER;
220 gc.weightx = 0.0;
221 gc.weighty = 0.0;
222 UndecideAction undecidedAction = new UndecideAction();
223 mergedTable.getSelectionModel().addListSelectionListener(undecidedAction);
224 JButton btnUndecide = new JButton(undecidedAction);
225 btnUndecide.setName("button.undecide");
226 add(btnUndecide, gc);
227
228 }
229
230 public TagMerger() {
231 model = new TagMergeModel();
232 build();
233 }
234
235 /**
236 * replies the model used by this tag merger
237 *
238 * @return the model
239 */
240 public TagMergeModel getModel() {
241 return model;
242 }
243
244 /**
245 * Keeps the currently selected tags in my table in the list of merged tags.
246 *
247 */
248 class KeepMineAction extends AbstractAction implements ListSelectionListener {
249 public KeepMineAction() {
250 ImageIcon icon = ImageProvider.get("dialogs/conflict", "tagkeepmine.png");
251 if (icon != null) {
252 putValue(Action.SMALL_ICON, icon);
253 putValue(Action.NAME, "");
254 } else {
255 putValue(Action.NAME, tr(">"));
256 }
257 putValue(Action.SHORT_DESCRIPTION, tr("Keep the selected key/value pairs from the local dataset"));
258 setEnabled(false);
259 }
260
261 public void actionPerformed(ActionEvent arg0) {
262 int rows[] = mineTable.getSelectedRows();
263 if (rows == null || rows.length == 0)
264 return;
265 model.decide(rows, MergeDecisionType.KEEP_MINE);
266 }
267
268 public void valueChanged(ListSelectionEvent e) {
269 setEnabled(mineTable.getSelectedRowCount() > 0);
270 }
271 }
272
273 /**
274 * Keeps the currently selected tags in their table in the list of merged tags.
275 *
276 */
277 class KeepTheirAction extends AbstractAction implements ListSelectionListener {
278 public KeepTheirAction() {
279 ImageIcon icon = ImageProvider.get("dialogs/conflict", "tagkeeptheir.png");
280 if (icon != null) {
281 putValue(Action.SMALL_ICON, icon);
282 putValue(Action.NAME, "");
283 } else {
284 putValue(Action.NAME, tr(">"));
285 }
286 putValue(Action.SHORT_DESCRIPTION, tr("Keep the selected key/value pairs from the server dataset"));
287 setEnabled(false);
288 }
289
290 public void actionPerformed(ActionEvent arg0) {
291 int rows[] = theirTable.getSelectedRows();
292 if (rows == null || rows.length == 0)
293 return;
294 model.decide(rows, MergeDecisionType.KEEP_THEIR);
295 }
296
297 public void valueChanged(ListSelectionEvent e) {
298 setEnabled(theirTable.getSelectedRowCount() > 0);
299 }
300 }
301
302 /**
303 * Synchronizes scrollbar adjustments between a set of
304 * {@see Adjustable}s. Whenever the adjustment of one of
305 * the registerd Adjustables is updated the adjustment of
306 * the other registered Adjustables is adjusted too.
307 *
308 */
309 class AdjustmentSynchronizer implements AdjustmentListener {
310 private final ArrayList<Adjustable> synchronizedAdjustables;
311
312 public AdjustmentSynchronizer() {
313 synchronizedAdjustables = new ArrayList<Adjustable>();
314 }
315
316 public void synchronizeAdjustment(Adjustable adjustable) {
317 if (adjustable == null)
318 return;
319 if (synchronizedAdjustables.contains(adjustable))
320 return;
321 synchronizedAdjustables.add(adjustable);
322 adjustable.addAdjustmentListener(this);
323 }
324
325 public void adjustmentValueChanged(AdjustmentEvent e) {
326 for (Adjustable a : synchronizedAdjustables) {
327 if (a != e.getAdjustable()) {
328 a.setValue(e.getValue());
329 }
330 }
331 }
332 }
333
334 /**
335 * Handler for double clicks on entries in the three tag tables.
336 *
337 */
338 class DoubleClickAdapter extends MouseAdapter {
339
340 @Override
341 public void mouseClicked(MouseEvent e) {
342 if (e.getClickCount() != 2)
343 return;
344 JTable table = null;
345 MergeDecisionType mergeDecision;
346
347 if (e.getSource() == mineTable) {
348 table = mineTable;
349 mergeDecision = MergeDecisionType.KEEP_MINE;
350 } else if (e.getSource() == theirTable) {
351 table = theirTable;
352 mergeDecision = MergeDecisionType.KEEP_THEIR;
353 } else if (e.getSource() == mergedTable) {
354 table = mergedTable;
355 mergeDecision = MergeDecisionType.UNDECIDED;
356 } else
357 // double click in another component; shouldn't happen,
358 // but just in case
359 return;
360 int row = table.rowAtPoint(e.getPoint());
361 model.decide(row, mergeDecision);
362 }
363 }
364
365 /**
366 * Sets the currently selected tags in the table of merged tags to state
367 * {@see MergeDecisionType#UNDECIDED}
368 *
369 */
370 class UndecideAction extends AbstractAction implements ListSelectionListener {
371
372 public UndecideAction() {
373 ImageIcon icon = ImageProvider.get("dialogs/conflict", "tagundecide.png");
374 if (icon != null) {
375 putValue(Action.SMALL_ICON, icon);
376 putValue(Action.NAME, "");
377 } else {
378 putValue(Action.NAME, tr("Undecide"));
379 }
380 putValue(SHORT_DESCRIPTION, tr("Mark the selected tags as undecided"));
381 setEnabled(false);
382 }
383
384 public void actionPerformed(ActionEvent arg0) {
385 int rows[] = mergedTable.getSelectedRows();
386 if (rows == null || rows.length == 0)
387 return;
388 model.decide(rows, MergeDecisionType.UNDECIDED);
389 }
390
391 public void valueChanged(ListSelectionEvent e) {
392 setEnabled(mergedTable.getSelectedRowCount() > 0);
393 }
394 }
395}
Note: See TracBrowser for help on using the repository browser.