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

Last change on this file since 10113 was 9136, checked in by Don-vip, 9 years ago

sonar fixes + javadoc

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