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

Last change on this file since 2621 was 2621, checked in by Gubaer, 14 years ago

Moved layer listener management from Layer to MapView
Made sure that listeners also unregister when they register for layer change events.

This will certainly break plugins. Plugin updates will follow later.

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