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

Last change on this file since 2710 was 2710, checked in by stoecker, 14 years ago

close #4222 - unify design of right menus again

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