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

Last change on this file since 1954 was 1954, checked in by Gubaer, 15 years ago

Moved package org.openstreetmap.josm.gui.conflict to org.openstreetmap.josm.gui.conflict.pair

File size: 13.0 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
211 DoubleClickAdapter dblClickAdapter = new DoubleClickAdapter();
212 mineTable.addMouseListener(dblClickAdapter);
213 theirTable.addMouseListener(dblClickAdapter);
214
215
216 gc.gridx = 2;
217 gc.gridy = 2;
218 gc.gridwidth = 1;
219 gc.gridheight = 1;
220 gc.fill = GridBagConstraints.NONE;
221 gc.anchor = GridBagConstraints.CENTER;
222 gc.weightx = 0.0;
223 gc.weighty = 0.0;
224 UndecideAction undecidedAction = new UndecideAction();
225 mergedTable.getSelectionModel().addListSelectionListener(undecidedAction);
226 JButton btnUndecide = new JButton(undecidedAction);
227 btnUndecide.setName("button.undecide");
228 add(btnUndecide, gc);
229
230 }
231
232 public TagMerger() {
233 model = new TagMergeModel();
234 build();
235 }
236
237 /**
238 * replies the model used by this tag merger
239 *
240 * @return the model
241 */
242 public TagMergeModel getModel() {
243 return model;
244 }
245
246 /**
247 * Keeps the currently selected tags in my table in the list of merged tags.
248 *
249 */
250 class KeepMineAction extends AbstractAction implements ListSelectionListener {
251 public KeepMineAction() {
252 ImageIcon icon = ImageProvider.get("dialogs/conflict", "tagkeepmine.png");
253 if (icon != null) {
254 putValue(Action.SMALL_ICON, icon);
255 putValue(Action.NAME, "");
256 } else {
257 putValue(Action.NAME, tr(">"));
258 }
259 putValue(Action.SHORT_DESCRIPTION, tr("Keep the selected key/value pairs from the local dataset"));
260 setEnabled(false);
261 }
262
263 public void actionPerformed(ActionEvent arg0) {
264 int rows[] = mineTable.getSelectedRows();
265 if (rows == null || rows.length == 0)
266 return;
267 model.decide(rows, MergeDecisionType.KEEP_MINE);
268 }
269
270 public void valueChanged(ListSelectionEvent e) {
271 setEnabled(mineTable.getSelectedRowCount() > 0);
272 }
273 }
274
275 /**
276 * Keeps the currently selected tags in their table in the list of merged tags.
277 *
278 */
279 class KeepTheirAction extends AbstractAction implements ListSelectionListener {
280 public KeepTheirAction() {
281 ImageIcon icon = ImageProvider.get("dialogs/conflict", "tagkeeptheir.png");
282 if (icon != null) {
283 putValue(Action.SMALL_ICON, icon);
284 putValue(Action.NAME, "");
285 } else {
286 putValue(Action.NAME, tr(">"));
287 }
288 putValue(Action.SHORT_DESCRIPTION, tr("Keep the selected key/value pairs from the server dataset"));
289 setEnabled(false);
290 }
291
292 public void actionPerformed(ActionEvent arg0) {
293 int rows[] = theirTable.getSelectedRows();
294 if (rows == null || rows.length == 0)
295 return;
296 model.decide(rows, MergeDecisionType.KEEP_THEIR);
297 }
298
299 public void valueChanged(ListSelectionEvent e) {
300 setEnabled(theirTable.getSelectedRowCount() > 0);
301 }
302 }
303
304 /**
305 * Synchronizes scrollbar adjustments between a set of
306 * {@see Adjustable}s. Whenever the adjustment of one of
307 * the registerd Adjustables is updated the adjustment of
308 * the other registered Adjustables is adjusted too.
309 *
310 */
311 class AdjustmentSynchronizer implements AdjustmentListener {
312 private final ArrayList<Adjustable> synchronizedAdjustables;
313
314 public AdjustmentSynchronizer() {
315 synchronizedAdjustables = new ArrayList<Adjustable>();
316 }
317
318 public void synchronizeAdjustment(Adjustable adjustable) {
319 if (adjustable == null)
320 return;
321 if (synchronizedAdjustables.contains(adjustable))
322 return;
323 synchronizedAdjustables.add(adjustable);
324 adjustable.addAdjustmentListener(this);
325 }
326
327 public void adjustmentValueChanged(AdjustmentEvent e) {
328 for (Adjustable a : synchronizedAdjustables) {
329 if (a != e.getAdjustable()) {
330 a.setValue(e.getValue());
331 }
332 }
333 }
334 }
335
336 /**
337 * Handler for double clicks on entries in the three tag tables.
338 *
339 */
340 class DoubleClickAdapter extends MouseAdapter {
341
342 @Override
343 public void mouseClicked(MouseEvent e) {
344 if (e.getClickCount() != 2)
345 return;
346 JTable table = null;
347 MergeDecisionType mergeDecision;
348
349 if (e.getSource() == mineTable) {
350 table = mineTable;
351 mergeDecision = MergeDecisionType.KEEP_MINE;
352 } else if (e.getSource() == theirTable) {
353 table = theirTable;
354 mergeDecision = MergeDecisionType.KEEP_THEIR;
355 } else if (e.getSource() == mergedTable) {
356 table = mergedTable;
357 mergeDecision = MergeDecisionType.UNDECIDED;
358 } else
359 // double click in another component; shouldn't happen,
360 // but just in case
361 return;
362 int row = table.rowAtPoint(e.getPoint());
363 model.decide(row, mergeDecision);
364 }
365 }
366
367 /**
368 * Sets the currently selected tags in the table of merged tags to state
369 * {@see MergeDecisionType#UNDECIDED}
370 *
371 */
372 class UndecideAction extends AbstractAction implements ListSelectionListener {
373
374 public UndecideAction() {
375 ImageIcon icon = ImageProvider.get("dialogs/conflict", "tagundecide.png");
376 if (icon != null) {
377 putValue(Action.SMALL_ICON, icon);
378 putValue(Action.NAME, "");
379 } else {
380 putValue(Action.NAME, tr("Undecide"));
381 }
382 putValue(SHORT_DESCRIPTION, tr("Mark the selected tags as undecided"));
383 setEnabled(false);
384 }
385
386 public void actionPerformed(ActionEvent arg0) {
387 int rows[] = mergedTable.getSelectedRows();
388 if (rows == null || rows.length == 0)
389 return;
390 model.decide(rows, MergeDecisionType.UNDECIDED);
391 }
392
393 public void valueChanged(ListSelectionEvent e) {
394 setEnabled(mergedTable.getSelectedRowCount() > 0);
395 }
396 }
397}
Note: See TracBrowser for help on using the repository browser.