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

Last change on this file since 6085 was 6085, checked in by Don-vip, 11 years ago

see #8902 - c-like array definitions changed to java-like (patch by shinigami)

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