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

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

fix EDT violations

  • Property svn:eol-style set to native
File size: 12.5 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 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
109 refresh(newSelection);
110 }
111
112 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
113 if (newLayer instanceof OsmDataLayer) {
114 refresh(((OsmDataLayer) newLayer).data.getAllSelected());
115 } else {
116 refresh(null);
117 }
118 }
119
120 public void layerAdded(Layer newLayer) {
121 // do nothing
122 }
123
124 public void layerRemoved(Layer oldLayer) {
125 // do nothing
126 }
127
128 public void refresh(Collection<? extends OsmPrimitive> fromPrimitives) {
129 model.populate(fromPrimitives);
130 GuiHelper.runInEDT(new Runnable() {
131 @Override
132 public void run() {
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
142 @Override
143 public void showDialog() {
144 super.showDialog();
145 Layer layer = Main.main.getActiveLayer();
146 if (layer instanceof OsmDataLayer) {
147 refresh(((OsmDataLayer)layer).data.getAllSelected());
148 }
149
150 }
151
152 class SelectUsersPrimitivesAction extends AbstractAction implements ListSelectionListener{
153 public SelectUsersPrimitivesAction() {
154 putValue(NAME, tr("Select"));
155 putValue(SHORT_DESCRIPTION, tr("Select objects submitted by this user"));
156 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
157 updateEnabledState();
158 }
159
160 public void select() {
161 int indexes[] = userTable.getSelectedRows();
162 if (indexes == null || indexes.length == 0) return;
163 model.selectPrimitivesOwnedBy(userTable.getSelectedRows());
164 }
165
166 public void actionPerformed(ActionEvent e) {
167 select();
168 }
169
170 protected void updateEnabledState() {
171 setEnabled(userTable != null && userTable.getSelectedRowCount() > 0);
172 }
173
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 public ShowUserInfoAction() {
185 super(false);
186 putValue(NAME, tr("Show info"));
187 putValue(SHORT_DESCRIPTION, tr("Launches a browser with information about the user"));
188 putValue(SMALL_ICON, ImageProvider.get("about"));
189 updateEnabledState();
190 }
191
192 @Override
193 public void actionPerformed(ActionEvent e) {
194 int rows[] = userTable.getSelectedRows();
195 if (rows == null || rows.length == 0) return;
196 List<User> users = model.getSelectedUsers(rows);
197 if (users.isEmpty()) return;
198 if (users.size() > 10) {
199 System.out.println(tr("Warning: only launching info browsers for the first {0} of {1} selected users", 10, users.size()));
200 }
201 int num = Math.min(10, users.size());
202 Iterator<User> it = users.iterator();
203 while(it.hasNext() && num > 0) {
204 String url = createInfoUrl(it.next());
205 if (url == null) {
206 break;
207 }
208 launchBrowser(url);
209 num--;
210 }
211 }
212
213 @Override
214 protected String createInfoUrl(Object infoObject) {
215 User user = (User)infoObject;
216 try {
217 return getBaseUserUrl() + "/" + URLEncoder.encode(user.getName(), "UTF-8").replaceAll("\\+", "%20");
218 } catch(UnsupportedEncodingException e) {
219 e.printStackTrace();
220 JOptionPane.showMessageDialog(
221 Main.parent,
222 tr("<html>Failed to create an URL because the encoding ''{0}''<br>"
223 + "was missing on this system.</html>", "UTF-8"),
224 tr("Missing encoding"),
225 JOptionPane.ERROR_MESSAGE
226 );
227 return null;
228 }
229 }
230
231 @Override
232 protected void updateEnabledState() {
233 setEnabled(userTable != null && userTable.getSelectedRowCount() > 0);
234 }
235
236 public void valueChanged(ListSelectionEvent e) {
237 updateEnabledState();
238 }
239 }
240
241 class DoubleClickAdapter extends MouseAdapter {
242 @Override
243 public void mouseClicked(MouseEvent e) {
244 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount()==2) {
245 selectionUsersPrimitivesAction.select();
246 }
247 }
248 }
249
250 /**
251 * Action for selecting the primitives contributed by the currently selected
252 * users.
253 *
254 */
255 private static class UserInfo implements Comparable<UserInfo> {
256 public User user;
257 public int count;
258 public double percent;
259 UserInfo(User user, int count, double percent) {
260 this.user=user;
261 this.count=count;
262 this.percent = percent;
263 }
264 public int compareTo(UserInfo o) {
265 if (count < o.count) return 1;
266 if (count > o.count) return -1;
267 if (user== null || user.getName() == null) return 1;
268 if (o.user == null || o.user.getName() == null) return -1;
269 return user.getName().compareTo(o.user.getName());
270 }
271
272 public String getName() {
273 if (user == null)
274 return tr("<new object>");
275 return user.getName();
276 }
277 }
278
279 /**
280 * The table model for the users
281 *
282 */
283 static class UserTableModel extends DefaultTableModel {
284 private ArrayList<UserInfo> data;
285
286 public UserTableModel() {
287 setColumnIdentifiers(new String[]{tr("Author"),tr("# Objects"),"%"});
288 data = new ArrayList<UserInfo>();
289 }
290
291 protected Map<User, Integer> computeStatistics(Collection<? extends OsmPrimitive> primitives) {
292 HashMap<User, Integer> ret = new HashMap<User, Integer>();
293 if (primitives == null || primitives.isEmpty()) return ret;
294 for (OsmPrimitive primitive: primitives) {
295 if (ret.containsKey(primitive.getUser())) {
296 ret.put(primitive.getUser(), ret.get(primitive.getUser()) + 1);
297 } else {
298 ret.put(primitive.getUser(), 1);
299 }
300 }
301 return ret;
302 }
303
304 public void populate(Collection<? extends OsmPrimitive> primitives) {
305 Map<User,Integer> statistics = computeStatistics(primitives);
306 data.clear();
307 if (primitives != null) {
308 for (Map.Entry<User, Integer> entry: statistics.entrySet()) {
309 data.add(new UserInfo(entry.getKey(), entry.getValue(), (double)entry.getValue() / (double)primitives.size()));
310 }
311 }
312 Collections.sort(data);
313 GuiHelper.runInEDTAndWait(new Runnable() {
314 @Override
315 public void run() {
316 fireTableDataChanged();
317 }
318 });
319 }
320
321 @Override
322 public int getRowCount() {
323 if (data == null) return 0;
324 return data.size();
325 }
326
327 @Override
328 public Object getValueAt(int row, int column) {
329 UserInfo info = data.get(row);
330 switch(column) {
331 case 0: /* author */ return info.getName() == null ? "" : info.getName();
332 case 1: /* count */ return info.count;
333 case 2: /* percent */ return NumberFormat.getPercentInstance().format(info.percent);
334 }
335 return null;
336 }
337
338 @Override
339 public boolean isCellEditable(int row, int column) {
340 return false;
341 }
342
343 public void selectPrimitivesOwnedBy(int [] rows) {
344 Set<User> users= new HashSet<User>();
345 for (int index: rows) {
346 users.add(data.get(index).user);
347 }
348 Collection<OsmPrimitive> selected = Main.main.getCurrentDataSet().getAllSelected();
349 Collection<OsmPrimitive> byUser = new LinkedList<OsmPrimitive>();
350 for (OsmPrimitive p : selected) {
351 if (users.contains(p.getUser())) {
352 byUser.add(p);
353 }
354 }
355 Main.main.getCurrentDataSet().setSelected(byUser);
356 }
357
358 public List<User> getSelectedUsers(int rows[]) {
359 LinkedList<User> ret = new LinkedList<User>();
360 if (rows == null || rows.length == 0) 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.