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

Last change on this file since 12301 was 12279, checked in by Don-vip, 7 years ago

sonar - squid:S3878 - Arrays should not be created for varargs parameters

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