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

Last change on this file since 4072 was 4072, checked in by jttt, 13 years ago

Fix #6301 Check for null mandatory in TableCellRenderer.getTableCellRendererComponent

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