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

Last change on this file since 3128 was 3071, checked in by mjulius, 14 years ago

fixes #4648 - User panel not showing data when toggled from hidden to shown

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