source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java@ 10478

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

see #13001 - replace calls to Main.main.getCurrentDataSet() by Main.getLayerManager().getEditDataSet()

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.awt.event.MouseAdapter;
10import java.awt.event.MouseEvent;
11import java.text.NumberFormat;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.HashMap;
17import java.util.HashSet;
18import java.util.Iterator;
19import java.util.LinkedList;
20import java.util.List;
21import java.util.Map;
22import java.util.Set;
23
24import javax.swing.AbstractAction;
25import javax.swing.JTable;
26import javax.swing.ListSelectionModel;
27import javax.swing.event.ListSelectionEvent;
28import javax.swing.event.ListSelectionListener;
29import javax.swing.table.DefaultTableModel;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.actions.AbstractInfoAction;
33import org.openstreetmap.josm.data.SelectionChangedListener;
34import org.openstreetmap.josm.data.osm.DataSet;
35import org.openstreetmap.josm.data.osm.OsmPrimitive;
36import org.openstreetmap.josm.data.osm.User;
37import org.openstreetmap.josm.gui.SideButton;
38import org.openstreetmap.josm.gui.layer.Layer;
39import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
40import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
41import org.openstreetmap.josm.gui.layer.OsmDataLayer;
42import org.openstreetmap.josm.gui.util.GuiHelper;
43import org.openstreetmap.josm.tools.ImageProvider;
44import org.openstreetmap.josm.tools.OpenBrowser;
45import org.openstreetmap.josm.tools.Shortcut;
46import org.openstreetmap.josm.tools.Utils;
47
48/**
49 * Displays a dialog with all users who have last edited something in the
50 * selection area, along with the number of objects.
51 *
52 */
53public class UserListDialog extends ToggleDialog implements SelectionChangedListener, ActiveLayerChangeListener {
54
55 /**
56 * The display list.
57 */
58 private JTable userTable;
59 private UserTableModel model;
60 private SelectUsersPrimitivesAction selectionUsersPrimitivesAction;
61 private ShowUserInfoAction showUserInfoAction;
62
63 /**
64 * Constructs a new {@code UserListDialog}.
65 */
66 public UserListDialog() {
67 super(tr("Authors"), "userlist", tr("Open a list of people working on the selected objects."),
68 Shortcut.registerShortcut("subwindow:authors", tr("Toggle: {0}", tr("Authors")), KeyEvent.VK_A, Shortcut.ALT_SHIFT), 150);
69 build();
70 }
71
72 @Override
73 public void showNotify() {
74 DataSet.addSelectionListener(this);
75 Main.getLayerManager().addActiveLayerChangeListener(this);
76 }
77
78 @Override
79 public void hideNotify() {
80 Main.getLayerManager().removeActiveLayerChangeListener(this);
81 DataSet.removeSelectionListener(this);
82 }
83
84 protected void build() {
85 model = new UserTableModel();
86 userTable = new JTable(model);
87 userTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
88 userTable.addMouseListener(new DoubleClickAdapter());
89
90 // -- select users primitives action
91 //
92 selectionUsersPrimitivesAction = new SelectUsersPrimitivesAction();
93 userTable.getSelectionModel().addListSelectionListener(selectionUsersPrimitivesAction);
94
95 // -- info action
96 //
97 showUserInfoAction = new ShowUserInfoAction();
98 userTable.getSelectionModel().addListSelectionListener(showUserInfoAction);
99
100 createLayout(userTable, true, Arrays.asList(new SideButton[] {
101 new SideButton(selectionUsersPrimitivesAction),
102 new SideButton(showUserInfoAction)
103 }));
104 }
105
106 /**
107 * Called when the selection in the dataset changed.
108 * @param newSelection The new selection array.
109 */
110 @Override
111 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
112 refresh(newSelection);
113 }
114
115 @Override
116 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
117 Layer activeLayer = e.getSource().getActiveLayer();
118 if (activeLayer instanceof OsmDataLayer) {
119 refresh(((OsmDataLayer) activeLayer).data.getAllSelected());
120 } else {
121 refresh(null);
122 }
123 }
124
125 /**
126 * Refreshes user list from given collection of OSM primitives.
127 * @param fromPrimitives OSM primitives to fetch users from
128 */
129 public void refresh(Collection<? extends OsmPrimitive> fromPrimitives) {
130 model.populate(fromPrimitives);
131 GuiHelper.runInEDT(new Runnable() {
132 @Override
133 public void run() {
134 if (model.getRowCount() != 0) {
135 setTitle(trn("{0} Author", "{0} Authors", model.getRowCount(), model.getRowCount()));
136 } else {
137 setTitle(tr("Authors"));
138 }
139 }
140 });
141 }
142
143 @Override
144 public void showDialog() {
145 super.showDialog();
146 Layer layer = Main.getLayerManager().getActiveLayer();
147 if (layer instanceof OsmDataLayer) {
148 refresh(((OsmDataLayer) layer).data.getAllSelected());
149 }
150 }
151
152 class SelectUsersPrimitivesAction extends AbstractAction implements ListSelectionListener {
153
154 /**
155 * Constructs a new {@code SelectUsersPrimitivesAction}.
156 */
157 SelectUsersPrimitivesAction() {
158 putValue(NAME, tr("Select"));
159 putValue(SHORT_DESCRIPTION, tr("Select objects submitted by this user"));
160 new ImageProvider("dialogs", "select").getResource().attachImageIcon(this, true);
161 updateEnabledState();
162 }
163
164 public void select() {
165 int[] indexes = userTable.getSelectedRows();
166 if (indexes == null || indexes.length == 0)
167 return;
168 model.selectPrimitivesOwnedBy(userTable.getSelectedRows());
169 }
170
171 @Override
172 public void actionPerformed(ActionEvent e) {
173 select();
174 }
175
176 protected void updateEnabledState() {
177 setEnabled(userTable != null && userTable.getSelectedRowCount() > 0);
178 }
179
180 @Override
181 public void valueChanged(ListSelectionEvent e) {
182 updateEnabledState();
183 }
184 }
185
186 /**
187 * Action for launching the info page of a user.
188 */
189 class ShowUserInfoAction extends AbstractInfoAction implements ListSelectionListener {
190
191 ShowUserInfoAction() {
192 super(false);
193 putValue(NAME, tr("Show info"));
194 putValue(SHORT_DESCRIPTION, tr("Launches a browser with information about the user"));
195 new ImageProvider("help/internet").getResource().attachImageIcon(this, true);
196 updateEnabledState();
197 }
198
199 @Override
200 public void actionPerformed(ActionEvent e) {
201 int[] rows = userTable.getSelectedRows();
202 if (rows == null || rows.length == 0)
203 return;
204 List<User> users = model.getSelectedUsers(rows);
205 if (users.isEmpty())
206 return;
207 if (users.size() > 10) {
208 Main.warn(tr("Only launching info browsers for the first {0} of {1} selected users", 10, users.size()));
209 }
210 int num = Math.min(10, users.size());
211 Iterator<User> it = users.iterator();
212 while (it.hasNext() && num > 0) {
213 String url = createInfoUrl(it.next());
214 if (url == null) {
215 break;
216 }
217 OpenBrowser.displayUrl(url);
218 num--;
219 }
220 }
221
222 @Override
223 protected String createInfoUrl(Object infoObject) {
224 if (infoObject instanceof User) {
225 User user = (User) infoObject;
226 return Main.getBaseUserUrl() + '/' + Utils.encodeUrl(user.getName()).replaceAll("\\+", "%20");
227 } else {
228 return null;
229 }
230 }
231
232 @Override
233 protected void updateEnabledState() {
234 setEnabled(userTable != null && userTable.getSelectedRowCount() > 0);
235 }
236
237 @Override
238 public void valueChanged(ListSelectionEvent e) {
239 updateEnabledState();
240 }
241 }
242
243 class DoubleClickAdapter extends MouseAdapter {
244 @Override
245 public void mouseClicked(MouseEvent e) {
246 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
247 selectionUsersPrimitivesAction.select();
248 }
249 }
250 }
251
252 /**
253 * Action for selecting the primitives contributed by the currently selected users.
254 *
255 */
256 private static class UserInfo implements Comparable<UserInfo> {
257 public final User user;
258 public final int count;
259 public final double percent;
260
261 UserInfo(User user, int count, double percent) {
262 this.user = user;
263 this.count = count;
264 this.percent = percent;
265 }
266
267 @Override
268 public int compareTo(UserInfo o) {
269 if (count < o.count)
270 return 1;
271 if (count > o.count)
272 return -1;
273 if (user == null || user.getName() == null)
274 return 1;
275 if (o.user == null || o.user.getName() == null)
276 return -1;
277 return user.getName().compareTo(o.user.getName());
278 }
279
280 public String getName() {
281 if (user == null)
282 return tr("<new object>");
283 return user.getName();
284 }
285 }
286
287 /**
288 * The table model for the users
289 *
290 */
291 static class UserTableModel extends DefaultTableModel {
292 private final transient List<UserInfo> data;
293
294 UserTableModel() {
295 setColumnIdentifiers(new String[]{tr("Author"), tr("# Objects"), "%"});
296 data = new ArrayList<>();
297 }
298
299 protected Map<User, Integer> computeStatistics(Collection<? extends OsmPrimitive> primitives) {
300 Map<User, Integer> ret = new HashMap<>();
301 if (primitives == null || primitives.isEmpty())
302 return ret;
303 for (OsmPrimitive primitive: primitives) {
304 if (ret.containsKey(primitive.getUser())) {
305 ret.put(primitive.getUser(), ret.get(primitive.getUser()) + 1);
306 } else {
307 ret.put(primitive.getUser(), 1);
308 }
309 }
310 return ret;
311 }
312
313 public void populate(Collection<? extends OsmPrimitive> primitives) {
314 Map<User, Integer> statistics = computeStatistics(primitives);
315 data.clear();
316 if (primitives != null) {
317 for (Map.Entry<User, Integer> entry: statistics.entrySet()) {
318 data.add(new UserInfo(entry.getKey(), entry.getValue(), (double) entry.getValue() / (double) primitives.size()));
319 }
320 }
321 Collections.sort(data);
322 GuiHelper.runInEDTAndWait(new Runnable() {
323 @Override
324 public void run() {
325 fireTableDataChanged();
326 }
327 });
328 }
329
330 @Override
331 public int getRowCount() {
332 if (data == null)
333 return 0;
334 return data.size();
335 }
336
337 @Override
338 public Object getValueAt(int row, int column) {
339 UserInfo info = data.get(row);
340 switch(column) {
341 case 0: /* author */ return info.getName() == null ? "" : info.getName();
342 case 1: /* count */ return info.count;
343 case 2: /* percent */ return NumberFormat.getPercentInstance().format(info.percent);
344 default: return null;
345 }
346 }
347
348 @Override
349 public boolean isCellEditable(int row, int column) {
350 return false;
351 }
352
353 public void selectPrimitivesOwnedBy(int[] rows) {
354 Set<User> users = new HashSet<>();
355 for (int index: rows) {
356 users.add(data.get(index).user);
357 }
358 Collection<OsmPrimitive> selected = Main.getLayerManager().getEditDataSet().getAllSelected();
359 Collection<OsmPrimitive> byUser = new LinkedList<>();
360 for (OsmPrimitive p : selected) {
361 if (users.contains(p.getUser())) {
362 byUser.add(p);
363 }
364 }
365 Main.getLayerManager().getEditDataSet().setSelected(byUser);
366 }
367
368 public List<User> getSelectedUsers(int[] rows) {
369 List<User> ret = new LinkedList<>();
370 if (rows == null || rows.length == 0)
371 return ret;
372 for (int row: rows) {
373 if (data.get(row).user == null) {
374 continue;
375 }
376 ret.add(data.get(row).user);
377 }
378 return ret;
379 }
380 }
381}
Note: See TracBrowser for help on using the repository browser.