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

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

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

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